27

I want to create my own EMailAddress class that acts like a string class.

So I like to do this

private EMailAddress _emailAddress = "[email protected]";

instead of

private EMailAddress _emailAddress = new EMailAddress("[email protected]");

Is there any way to accomplish what I want, or do I need to use the second alternative. Since string is sealed I can't use that, and the = operator can't be overloaded so I am out of ideas how to fix this....

10
  • 7
    If it looks like a string - it should be a string. I'm not sure it is possible and even is a good idea. Commented Aug 8, 2010 at 21:19
  • 2
    No one would expect to see that sort of thing. Just create your own class. Commented Aug 8, 2010 at 21:21
  • 9
    Just about everything can look like a strings, should we program only with strings? An email address has different properties to a string. "[email protected]" can be an email address and a string, "fish" can not be an email address. A HostName property or a CheckMxRecord method makes more sense for an email address than a string. At the same time it's close enough to a string to make implicit casting reasonable. Not sure it's a great idea (would need to know more about the class and its use), but it certainly isn't automatically a bad one. Commented Aug 8, 2010 at 23:53
  • 5
    On the contrary, this is a very good idea. I need this all time when working with DDD code. A strong type helps to make a difference between a "firstname" and a "lastname", for instance... In good old pascal there is a type MyType = type string; which allows this directly. I miss this in C#. I understand the question, and that creating a new class in such cases could be avoided. Commented Oct 1, 2016 at 8:58
  • 1
    To all who think this a bad idea, consider Enums. A significant portion of the value of Enums is to add type information to what are otherwise just integers. Commented May 25, 2019 at 19:30

6 Answers 6

36

You can, with an implicit conversion:

public class EMailAddress
{
    private string _address;

    public EMailAddress(string address)
    {
        _address = address;
    }

    public static implicit operator EMailAddress(string address)
    {
        // While not technically a requirement; see below why this is done.
        if (address == null)
            return null;

        return new EMailAddress(address);
    }
}

Implicit conversions should only be used if no data is lost in the conversion. Even then, I recommend you use this feature sparingly, because it can make your code more difficult to read.

In this example, the implicit operator returns null when a null string is passed. As Jon Hanna correctly commented, it is undesirable to have these two snippets of code behave differently:

// Will assign null to the reference
EMailAddress x = null;

// Would create an EMailAddress object containing a null string
string s = null;
EMailAddress y = s;
Sign up to request clarification or add additional context in comments.

6 Comments

This has EmailAddress x = null; behave differently to String s = null;EmailAddress x = s; which is strange. Better to have the implicit operator return null on null parameter.
Implicit conversions seem to defeat the whole idea of having a separate class.
@Anton, why? Do you think the fact that we can implicitly cast an int to double defeats the point of having them separately?
int/double is a different case. There are 3 reasons to have a separate EMailAddress type: adding methods (best accomplished with extensions); self-documenting code (using EMailAddress = System.String might help); and isolation of e-mail addresses from other strings for type safety. Implicit conversions completely defeat this third and (IMO) most important reason.
@Anton: you make an interesting point about the type safety. Personally, I would probably not create an implicit conversion for this case either. I do use explicit conversions between equivalent types on occasion, because it saves me from remembering what the conversion method is called :)
|
9

You do this by adding an implicit operator from string to your custom type.

class EMailAddress 
{
        // ...other members

        public static implicit operator EMailAddress (string address)
        {
            return new EMailAddress(address);
        }
}

However, I recommend using this sparingly.

2 Comments

that's really spiffy. I thought something like that existed. I should try not to forget about it again! EDIT: ah, I didn't catch your edit. why do you recommend use it sparingly? I guess I could always finish reading msdn page...
Sparingly = this violates the priciple of least astonishment. Programmers wouldn't expect this, therefore making code complicated and behave unexpectedly. BUT, the string class IS defective, and string violates POLA by being a reference type but acting like a value type. Many of my abstract generic class require parameter-less constructors, so I can't use String class in my generics. So I created a StringMutable class so that it can be used in my abstract classes, where : new()
5

I think that this question is a specific case of a more general question about creating type safety across a C# application. My example here is with 2 types of data: Prices and Weights. They have different units of measure so one should never try an assign a price to a weight or vice versa. Both under the covers are really decimal values. (I am ignoring the fact that there could be conversions like pounds to kg etc.) This same idea could be applied to strings with specific types like EmailAddress and UserLastName.

With some fairly boiler plate code one can make either explicit conversion or implicit conversions back and forth between the specific types: Price and Weight, and the underlying type Decimal.

public class Weight
{
    private readonly Decimal _value;

    public Weight(Decimal value)
    {
        _value = value;
    }

    public static explicit operator Weight(Decimal value)
    {
        return new Weight(value);
    }

    public static explicit operator Decimal(Weight value)
    {
        return value._value;
    }
};


 public class Price {
    private readonly Decimal _value;

    public Price(Decimal value) {
        _value = value;
    }

    public static explicit operator Price(Decimal value) {
        return new Price(value);
    }

    public static explicit operator Decimal(Price value)
    {
        return value._value;
    }
};

With the "explicit" operator overrides, one gets a more restrictive set of things one can do with these classes. You have to be manually case every time you change from one type to another. For example:

    public void NeedsPrice(Price aPrice)
    {
    }

    public void NeedsWeight(Weight aWeight)
    {
    }

    public void NeedsDecimal(Decimal aDecimal)
    {
    }
  public void ExplicitTest()
    {

        Price aPrice = (Price)1.23m;
        Decimal aDecimal = 3.4m;
        Weight aWeight = (Weight)132.0m;

        // ok
        aPrice = (Price)aDecimal;
        aDecimal = (Decimal)aPrice;

        // Errors need explicit case
        aPrice = aDecimal;
        aDecimal = aPrice;

        //ok
        aWeight = (Weight)aDecimal;
        aDecimal = (Decimal) aWeight;

        // Errors need explicit cast
        aWeight = aDecimal;
        aDecimal = aWeight;

        // Errors (no such conversion exists)
        aPrice = (Price)aWeight;
        aWeight = (Weight)aPrice;

        // Ok, but why would you ever do this.
        aPrice = (Price)(Decimal)aWeight;
        aWeight = (Weight)(Decimal)aPrice;

        NeedsPrice(aPrice);   //ok
        NeedsDecimal(aPrice); //error
        NeedsWeight(aPrice);  //error

        NeedsPrice(aDecimal);   //error
        NeedsDecimal(aDecimal); //ok
        NeedsWeight(aDecimal);  //error

        NeedsPrice(aWeight);   //error
        NeedsDecimal(aWeight); //error
        NeedsWeight(aWeight);  //ok
    }

Just changing the "explicit" operators to "implicit" operators by replacing the words "explicit" with "implicit" in the code, one can convert back and forth to the underlying Decimal class without any extra work. This makes Price and Weight behave more like a Decimal, but you still cannot change a Price to a Weight. This is usually the level of type safety I am looking for.

 public void ImplicitTest()
    {
        Price aPrice = 1.23m;
        Decimal aDecimal = 3.4m;
        Weight aWeight = 132.0m;

        // ok implicit cast
        aPrice = aDecimal;
        aDecimal = aPrice;

        // ok implicit cast
        aWeight = aDecimal;
        aDecimal = aWeight;

        // Errors 
        aPrice = aWeight;
        aWeight = aPrice;


        NeedsPrice(aPrice);   //ok
        NeedsDecimal(aPrice); //ok
        NeedsWeight(aPrice);  //error

        NeedsPrice(aDecimal);   //ok
        NeedsDecimal(aDecimal); //ok
        NeedsWeight(aDecimal);  //ok

        NeedsPrice(aWeight);   //error
        NeedsDecimal(aWeight); //ok
        NeedsWeight(aWeight);  //ok
    }    

When doing this for String instead of Decimal. I like the idea Thorarin's answer about checking for null and passing null back in the conversion. e.g.

public static implicit operator EMailAddress(string address)
{
    // Make
    //      EmailAddress myvar=null 
    // and
    //      string aNullString = null;
    //      EmailAddress myvar = aNullString;
    // give the same result.
    if (address == null)
        return null;

    return new EMailAddress(address);
}

To get these classes working as keys to Dictionary collections, you will also need to implement Equals, GetHashCode, operator ==, and operator !=

To make all of this easier, I made a class ValueType which I can extend, The ValueType class calls the base type for everything but the conversion operators.

Comments

3
  1. An implicit operator should cast a null to another null, except when the type cast to isn't nullable, in which case it should error on null.
  2. Please if you are writing something that holds a Uri, don't force people using it who have a Uri to do the work of obtaining the string for it themselves. Email addresses fit naturally to mailto: uris, so while this isn't quite an example of this, it's close.

Example:

public class EmailAddress
{
    private string _value;
    private static bool IsValidAddress(string address)
    {
        //whether to match RFC822 production or have something simpler,
        //but excluding valid but unrealistic addresses, is an impl. choice
        //out of scope.
        return true;
    }
    public EMailAddress(string value)
    {
        if(value == null)
            throw new ArgumentNullException();
        if(!IsValidAddress(value))
            throw new ArgumentException();
        _value = value;
    }
    public EmailAddress(Uri uri)
    {
        if(value == null)
            throw new ArgumentNullException();
        if(!uri.Scheme != "mailto")
            throw new ArgumentException();
        string extracted = uri.UserInfo + "@" + uri.Host;
        if(!IsValidAddress(extracted))
           throw new ArgumentException();
        _value = extracted;
    }
    public override string ToString()
    {
        return _value;
    }
    public static implicit operator EMailAddress(string value)
    {
        return value == null ? null : new EMailAddress(value);
    }
    public static implicit operator EMailAddress(Uri uri)
    {
        return value == null ? null : new EMailAddress(uri);
    }
}

3 Comments

Adding e-mail address validation to the class makes sense. However, implicit conversions that can fail are a bit of a code smell, imo.
Also, I'm a little bit disappointed that you didn't implement full RFC822 (or RFC5322 which supersedes it) validation ;)
Well full validation would just mean adding a really long Regex into the example, and often isn't what is really wanted anyway. As to whether it's a bad code smell, code smells are relative. It would lean me against allowing an implicit (and if I were creating this class myself, probably lean me against enough that I wouldn't do it), but I wouldn't see it as a definite no-no.
0

As you noted you can't inherit from string but you can extend it with extension methods.

So you can make some email specific extension methods to handle whatever it is you were thinking of putting in the EmailAddress class.

Comments

0

As a follow-up to @Thorarin's code:

You could use this as base code and add extra features that a normal string class would have:

public class VarChar
{
        private string _content;

        public VarChar(string argContent)
        {
            _content = argContent;
        }

        public static implicit operator VarChar(string argContent)
        {
            if (argContent == null)
                return null;

            return new VarChar(argContent);
        }

        public static implicit operator string(VarChar x) => x._content;

        public override string ToString()
        {
            return _content;
        }
}

So the following code just works:

VarChar tempVarChar = "test";

string tempTest = tempVarChar; // = "test"

var tempText = tempVarChar.ToString();

tempVarChar = tempVarChar + "_oke";

tempText = tempVarChar; // = "test_oke"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.