鉴于以下类别:
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
)?
答案 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()
不同的内容非常令人困惑。
其次,emailString1
和emailString2
不能与您声称的相同。 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
,你隐藏它。要查看ToString
和new
之间的差异,请阅读this SO question。