简体   繁体   English

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

[英]String interpolation in C# 6 and overridden ToString()

Given the following classes: 鉴于以下类别:

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;
    }
}

Why does: 为什么:

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

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

return a string of the type name ( Namespace.EmailAddress ), not the overridden ToString method ( joe@bloggs.com )? 返回类型名称的字符串( Namespace.EmailAddress ),而不是重写的ToString方法( joe@bloggs.com )?

Interpolation works as expected since your classes do not override Object.ToString() . 插值按预期工作,因为您的类不会覆盖Object.ToString() ValueBase defines a new method that hides Object.ToString instead of overriding it. ValueBase定义了一个隐藏 Object.ToString而不是覆盖它的方法。

Simply remove ToString from ValueBase . 只需从ValueBase删除ToString ValueBase In this case Email.Address will override Object.ToString correctly, and interpolation will return the desired result. 在这种情况下Email.Address将覆盖Object.ToString正确,和插值将返回所需的结果。

Specifically, changing ValueBase to this: 具体来说,将ValueBase更改为:

public abstract class ValueBase
{
}

Makes the test code 制作测试代码

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

return joe@bloggs.com 返回joe@bloggs.com

UPDATE UPDATE

As people suggested, the base ToString() method could be added to force implementers to implement a custom ToString method in their classes. 正如人们所建议的那样,可以添加基本的ToString()方法来强制实现者在他们的类中实现自定义ToString方法。 This can be achieved by defining an abstract override method. 这可以通过定义abstract override方法来实现。

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

Well, $"{email}" string is automatically converted to string.Format("{0}", email) and this method's second parameter is of type params object[] . 好吧, $"{email}"字符串会自动转换为string.Format("{0}", email) ,此方法的第二个参数类型为params object[] So it upcasts all values to object before invoking ToString() method. 因此,在调用ToString()方法之前,它会将所有值向上转换为object In your code you simply replace this method with new one inside ValueBase class, and override keyword inside EmailAddress class implements this abstract method and not the original object's one. 在您的代码中,您只需将此方法替换为ValueBase类中的新方法,并且EmailAddress类中的override关键字实现此抽象方法,而不是原始对象的方法。

You can easily test it if you cast your second value to object explicitly : 如果将第二个值明确地转换为object,则可以轻松测试它:

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

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

As you can see now emailString2 returns typename as well. 如您所见, emailString2返回typename。 You can remove ToString() method from abstract class and let EmailAdress class to implement object's ToString() or implement it there in abstract class. 您可以从抽象类中删除ToString()方法,并让EmailAdress类实现对象的ToString()或在抽象类中实现它。 For example : 例如 :

    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;
       }
    }

With this new code the output is as expected : 使用此新代码,输出符合预期:

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

Actually $"" Ultimately are using a internal method in the class StringBuilder. 实际上$“”最终在StringBuilder类中使用了一个内部方法。

StringBuilder.AppendFormatHelper(IFormatProvider provider, string format, ParamsArray args) StringBuilder.AppendFormatHelper(IFormatProvider提供程序,字符串格式,ParamsArray args)

The key code: 关键代码:

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

First off, hiding ToString is asking for trouble, dont do it; 首先,隐藏ToString要求麻烦,不要这样做; it is pretty confusing that string.Format("{0}", email) or $"{email} prints out something different than email.ToString() . string.Format("{0}", email)$"{email}打印出与email.ToString()不同的东西是相当令人困惑的。

Second, emailString1 and emailString2 can not be the same as you claim. 其次, emailString1emailString2不能与您声明的相同。 The output of email.ToString() is "joe@bloggs.com" , the output of $"{email}" is "{namespace}.EmailAddress" . email.ToString()的输出是"joe@bloggs.com"$"{email}"的输出是"{namespace}.EmailAddress"

Why the difference? 为什么不同? It's because you are hiding ToString in ValueBase . 这是因为你在ValueBase隐藏了 ToString A call of the type EmailAddress.ToString (a call through an EmailAddress typed reference) will call the new implementation of ToString but a call Object.ToString (a call through an object typed reference) will call the implementation in Object ; 调用EmailAddress.ToString类型(通过EmailAddress类型引用调用)将调用ToString的新实现,但调用Object.ToString (通过object类型引用调用)将调用Object的实现; $"{email}" is equivalent to string.Format("{0}", email) which is morally equivalent to string.Format("{0}", (object)mail) so you are really invoking ToString form an object typed reference; $"{email}"相当于string.Format("{0}", email) ,它在道德上等同于string.Format("{0}", (object)mail)所以你真的在调用ToString形成一个对象打字参考;

What you need to do is override ToString and you will get the expected behavior: 您需要做的是覆盖 ToString ,您将获得预期的行为:

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

Any call to an EmailAddress.ToString , no mater the type of the reference will call the overriden implementation. EmailAddress.ToString任何调用,无论引用的类型是什么,都会调用overriden实现。

Note that you seem to be conflating new and override : 请注意,您似乎在混淆newoverride

Why does ... return a string of the type name (Namespace.EmailAddress) , not the overridden ToString method ( joe@bloggs.com )? 为什么...返回类型名称的字符串(Namespace.EmailAddress) ,而不是重写的ToString方法( joe@bloggs.com )?

You are not overriding ToString , you are hiding it. 你没有覆盖 ToString ,你正在隐藏它。 To see the difference between new and override read this SO question . 要查看newoverride之间的区别,请阅读此SO问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM