C#6中的字符串插值和重写的ToString()

时间:2016-04-13 09:12:28

标签: c# string c#-6.0

鉴于以下类别:

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)?

4 个答案:

答案 0 :(得分:18)

插值按预期工作,因为您的类覆盖Bar#26820.super.foo#26823[B#26833](x#26836, y#26837)Object.ToString()定义隐藏 ValueBase方法,而不是覆盖它。

只需从Object.ToString移除ToString即可。在这种情况下,ValueBase会正确覆盖Email.Address,插值将返回所需的结果。

具体来说,将ValueBase更改为:

Object.ToString

制作测试代码

public abstract class ValueBase
{
}

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

<强>更新

正如人们所建议的那样,可以添加基础joe@bloggs.com方法以强制实施者在其类中实现自定义ToString()方法。这可以通过定义ToString方法来实现。

abstract override

答案 1 :(得分:6)

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

如果将第二个值明确地转换为对象,则可以轻松测试它:

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

答案 2 :(得分:3)

string emailString1 = $"{email}"; //call the Object.ToString() method
string emailString2 = email.ToString();//call your overrided ValueBase.ToString() method

实际上是$&#34;&#34;最终在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 :(得分:2)

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

其次,emailString1emailString2不能与您声称的相同。 email.ToString()的输出为"joe@bloggs.com"$"{email}"的输出为"{namespace}.EmailAddress"

为何与众不同?这是因为您在ToString隐藏 ValueBase。类型为EmailAddress.ToString的呼叫(通过EmailAddress类型参考调用)会调用ToString的新实现,但会调用Object.ToString(通过{{1 }} typed reference)将调用object中的实现; Object相当于$"{email}",它在道德上等同于string.Format("{0}", email),因此您实际上正在调用string.Format("{0}", (object)mail)形成对象类型的引用;

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

ToString

public abstract class ValueBase { public override abstract string ToString(); } 的任何调用,无论引用的类型是否都会调用覆盖实现。

请注意,您似乎正在混淆 new 覆盖

  

为什么...返回类型名称为EmailAddress.ToString的字符串,而不是重写的(Namespace.EmailAddress)方法(ToString)?

覆盖 joe@bloggs.com,你隐藏它。要查看ToStringnew之间的差异,请阅读this SO question