17

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("[email protected]");

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

return a string of the type name (Namespace.EmailAddress), not the overridden ToString method ([email protected])?

6
  • 4
    It is asking for trouble to create a new ToString method hiding the Object.ToString method that all types have. Commented Apr 13, 2016 at 9:26
  • What is the code of MailAddress? Commented Apr 13, 2016 at 9:29
  • You have two string there. Commented Apr 13, 2016 at 9:30
  • 2
    Use 'public abstract override string ToString();' in your base class. Commented Apr 13, 2016 at 13:31
  • 1
    Why define ToString at all in the base class if it doesn't really override anything? The only valid use I can think of is it to force implementers to override ToString? Commented Apr 14, 2016 at 7:38

4 Answers 4

25

Interpolation works as expected since your classes do not override Object.ToString(). ValueBase defines a new method that hides Object.ToString instead of overriding it.

Simply remove ToString from ValueBase. In this case Email.Address will override Object.ToString correctly, and interpolation will return the desired result.

Specifically, changing ValueBase to this:

public abstract class ValueBase
{
}

Makes the test code

var email = new EmailAddress("[email protected]");
string emailString1 = $"{email}";

return [email protected]

UPDATE

As people suggested, the base ToString() method could be added to force implementers to implement a custom ToString method in their classes. This can be achieved by defining an abstract override method.

public abstract class ValueBase
{
    public abstract override string ToString();
}
Sign up to request clarification or add additional context in comments.

2 Comments

It seems the OP wants to enforce a custom implementation of the ToString method. The easiest way would be to override ToString to call some other abstract method.
@Luaan nope, the easiest way would be public abstract override string ToString();
10

Well, $"{email}" string is automatically converted to string.Format("{0}", email) and this method's second parameter is of type params object[]. So it upcasts all values to object before invoking ToString() method. 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.

You can easily test it if you cast your second value to object explicitly :

var email = new EmailAddress("[email protected]");

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

As you can see now emailString2 returns typename as well. You can remove ToString() method from abstract class and let EmailAdress class to implement object's ToString() or implement it there in abstract class. 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 :

[email protected]
[email protected]

3 Comments

Coupled with public override abstract string ToString(); on the base type and it's working as expected. Thanks.
What is the point of the no-op override in the abstract class?
@PanagiotisKanavos that's to force the subclass to provide a ToString implementation
3
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.AppendFormatHelper(IFormatProvider provider, string format, 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()

Comments

2

First off, hiding ToString is asking for trouble, dont do it; it is pretty confusing that string.Format("{0}", email) or $"{email} prints out something different than email.ToString().

Second, emailString1 and emailString2 can not be the same as you claim. The output of email.ToString() is "[email protected]", the output of $"{email}" is "{namespace}.EmailAddress".

Why the difference? It's because you are hiding ToString in ValueBase. 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; $"{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;

What you need to do is override ToString and you will get the expected behavior:

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.

Note that you seem to be conflating new and override:

Why does ... return a string of the type name (Namespace.EmailAddress), not the overridden ToString method ([email protected])?

You are not overriding ToString, you are hiding it. To see the difference between new and override read this SO question.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.