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.
ToStringmethod hiding theObject.ToStringmethod that all types have.MailAddress?ToStringat 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 overrideToString?