【问题标题】:String interpolation in C# 6 and overridden ToString()C# 6 中的字符串插值和重写的 ToString()
【发布时间】:2016-08-04 06:33:36
【问题描述】:

给定以下类:

public abstract class ValueBase
{
    public new abstract string ToString();
}

public class EmailAddress : ValueBase
{
    public MailAddress MailAddress { get; }

    public EmailAddress([NotNull] string address)
    {
        MailAddress = new MailAddress(address);
    }

    public override string ToString()
    {
        return MailAddress.Address;
    }
}

为什么会这样:

var email = new EmailAddress("joe@bloggs.com");

string emailString1 = $"{email}";
string emailString2 = email.ToString();

返回类型名称的字符串 (Namespace.EmailAddress),而不是覆盖的 ToString 方法 (joe@bloggs.com)?

【问题讨论】:

  • 创建一个new ToString 方法隐藏所有类型都有的Object.ToString 方法是自找麻烦。
  • MailAddress的代码是什么?
  • 那里有两个字符串。
  • 使用'public abstract override string ToString();'在你的基类中。
  • 如果ToString 并没有真正覆盖任何东西,为什么还要在基类中定义它呢?我能想到的唯一有效用途是强制实现者覆盖ToString?

标签: c# string c#-6.0


【解决方案1】:

插值按预期工作,因为您的类覆盖Object.ToString()ValueBase 定义了一个 new 方法,该方法隐藏 Object.ToString 而不是覆盖它。

只需从ValueBase 中删除ToString。在这种情况下,Email.Address 将正确覆盖Object.ToString,并且插值将返回所需的结果。

具体来说,将 ValueBase 更改为:

public abstract class ValueBase
{
}

制作测试代码

var email = new EmailAddress("joe@bloggs.com");
string emailString1 = $"{email}";

返回joe@bloggs.com

更新

正如人们所建议的,可以添加基本的ToString() 方法来强制实现者在他们的类中实现自定义的ToString 方法。这可以通过定义abstract override 方法来实现。

public abstract class ValueBase
{
    public abstract override string ToString();
}

【讨论】:

  • 看来 OP 想要强制执行 ToString 方法的自定义实现。最简单的方法是重写 ToString 以调用一些 other 抽象方法。
  • @Luaan 不,最简单的方法是public abstract override string ToString();
【解决方案2】:
string emailString1 = $"{email}"; //call the Object.ToString() method
string emailString2 = email.ToString();//call your overrided ValueBase.ToString() method

其实$"" 最终都是使用StringBuilder类中的内部方法。

StringBuilder.AppendFormatHelper(IFormatProvider provider, string format, ParamsArray args)

关键代码:

 object obj = args[index3];
 //... bilibili About On Line 1590
  else if (obj != null)
    str = obj.ToString();// So it use the object.ToString()

【讨论】:

    【解决方案3】:

    首先,隐藏ToString是自找麻烦,不要这样做; string.Format("{0}", email)$"{email} 打印出的内容与 email.ToString() 不同,这非常令人困惑。

    第二,emailString1emailString2不能和你声称的一样。 email.ToString()的输出是"joe@bloggs.com"$"{email}"的输出是"{namespace}.EmailAddress"

    为什么不一样?这是因为你在ValueBase隐藏 ToStringEmailAddress.ToString 类型的调用(通过EmailAddress 类型引用的调用)将调用ToString 的新实现,但调用Object.ToString(通过object 类型引用的调用)将调用实现Object; $"{email}" 等价于 string.Format("{0}", email) 道德上等价于 string.Format("{0}", (object)mail) 所以你实际上是在调用 ToString 形成一个对象类型的引用;

    您需要做的是覆盖 ToString,您将获得预期的行为:

    public abstract class ValueBase
    {
        public override abstract string ToString();
    }
    

    EmailAddress.ToString 的任何调用,无论引用的类型如何,都会调用被覆盖的实现。

    请注意,您似乎将 newoverride 混为一谈:

    为什么 ... 返回类型名称为 (Namespace.EmailAddress) 的字符串,而不是被覆盖的 ToString 方法 (joe@bloggs.com)?

    你没有覆盖ToString,你隐藏它。要查看newoverride 之间的区别,请阅读this SO question

    【讨论】:

      【解决方案4】:

      好吧,$"{email}" 字符串会自动转换为string.Format("{0}", email),并且此方法的第二个参数是params object[] 类型。因此,在调用 ToString() 方法之前,它会将所有值向上转换为 object。在您的代码中,您只需将此方法替换为 ValueBase 类中的新方法,并且 EmailAddress 类中的 override 关键字实现此抽象方法,而不是原始对象的方法。

      如果您将第二个值明确地转换为对象,您可以轻松地对其进行测试:

      var email = new EmailAddress("joe@bloggs.com");
      
      string emailString1 = $"{email}";
      string emailString2 = ((object)email).ToString();
      

      正如你现在所看到的,emailString2 也返回 typename。您可以从抽象类中删除ToString() 方法,并让EmailAdress 类实现对象的ToString() 或在抽象类中实现它。例如:

          public abstract class ValueBase
          {
              // overrides object's ToString()
              public override string ToString()
              {
                  return base.ToString();
              }
          }
      
          public class EmailAddress : ValueBase
          {
             ...
      
             // overrides ValueBase's ToString()
             public override string ToString()
             {
                 return MailAddress.Address;
             }
          }
      

      有了这个新代码,输出如预期:

      joe@bloggs.com
      joe@bloggs.com
      

      【讨论】:

      • 在基本类型上加上public override abstract string ToString();,它按预期工作。谢谢。
      • 抽象类中无操作覆盖的意义何在?
      • @PanagiotisKanavos 强制子类提供ToString 实现
      猜你喜欢
      • 2015-10-24
      • 2015-02-14
      • 1970-01-01
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 2017-01-28
      • 2017-06-05
      • 2015-09-28
      相关资源
      最近更新 更多