Beginning C#: User-defined Type Conversion Operators

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

A type conversion is something you may, or may not do, on a regular basis. Either way, if you’re not familiar with the term, consider the following piece of code.…

int thirtyTwoBit = 123456789;
long sixtyFourBit = thirtyTwoBit;
Note: I’m entering my code into a standard .NET Console application in Visual Studio.

We started by declaring a 32-bit signed integer named thirtyTwoBit and gave it a value. Then, we assigned the value of that first integer to a 64-bit signed integer named sixtyFourBit. A conversation has taken place here, an implicit one thatich requires no special syntax, because we’ve gone from a 32-bit integer to a 64-bit one. This, however, was a type-safe conversion and no data is lost because we can easily put a 32-bit integer in to 64-bit integer.

But what about this piece of code?

short sixteenBit = thirtyTwoBit;

If you enter this snippet into a code editor with IntelliSense, you’ll mostly likely see an error here. At the very least, it shouldn’t compile. We are attempting to assign a 32-bit integer to a strongly typed 16-bit variable, and an implicit—type-safe—conversion cannot take place. But, if you insisted on doing such a conversion, there is a way. Take a look at this next line of code.…

short sixteenBit = (short)thirtyTwoBit;

Now, we have what is known as an explicit conversion, that uses a special bit of syntax know as a cast operator. However, as you probably will have guessed, there is a good chance we may lose some data in this conversion.

Defining Your Own Conversion Operator

Now that we’ve seen two possible ways of performing a conversion, how about creating your own conversions for use with your own types? Well, C# does offer you a way of doing this. Let’s say we have two structs, both with public fields; the first one exposes an int, and the second a long, like the following.

struct FirstValueType
{
   public int firstValue;

   public static implicit operator SecondValueType(FirstValueType firstType)
   {
      return new SecondValueType ()
      {
         firstValue = firstType.firstValue
      };
   }
}

struct SecondValueType
{
   public long firstValue;
}

Our FirstValueType also includes an implicit operator so we can perform an implicit conversion from FirstValueType to SecondValueType, which looks something like this.…

FirstValueType firstType = new FirstValueType()
{
   firstValue = 123456789
};

SecondValueType secondType = firstType;

Keep in mind that, as part of this conversion, we are converting a field that is a 32-bit integer, to a second type, which is a field that is a 64-bit integer. This is a safe type conversion and therefore an implicit conversion, and, as convention would have it, is the appropriate type of conversion operator to use.

How about a conversation in the other direction? Let’s go from our SecondValueType to our FirstValueType and make this change to our SecondValueType.…

struct SecondValueType
{
   public long secondValue;

   public static explicit operator FirstValueType(SecondValueType secondType)
   {
      return new FirstValueType()
      {
         firstValue = (int)secondType.secondValue
      };
   }
}

Which allows us to do something like.…

FirstValueType anotherFirstType = (FirstValueType)secondType;

Again, good coding convention dictates this is a good time to use an explicit conversion because we have the potential to lose some data in the conversion.

What if we wanted to do something like the following?

int number = firstType;

Well, add the following user-defined conversion operator to your FirstValueType, and allow for such a conversion.

public static implicit operator int(FirstValueType firstType)
{
   return firstType.firstValue;
}

Yes, this example is a bit of a fantasy in terms of real world usage; however, it highlights the use of good standards in that we have a good time to use an implicit conversion and an explicit one. I’ve also used Value Types in this example, but what we’ve done here does expand to a Class, too.

For example, let’s take a simple class that represents a person, which exposes a properties of type byte that represents the age of the person. But, we want a quick way to extract the age of the person as an int from this class; we can do the following.…

class Person
{
   public string Name { get; set; }

   public byte Age { get; set; }

   public static implicit operator int(Person person)
   {
      return person.Age;
   }
}

A Recent Application of the User-defined Explicit Conversion Operator

As we’ve talked about, explicit conversions are a good indication to the developer that a conversion runs the risk of losing data. However, and very recently, I came across a situation when this was acceptable.

Consider the following scenario.

A WebApi, and this is the Azure Management API I’m referring to, has several levels of resource hierarchy you can access through the API. These include Web sites, VMs, and more. However, at the highest possible level, and no matter what the resource was, they were presented as a very simple object. That object contained an id, a name, and a location. However, from the id, it is possible to determine what the resource is and get more data about that resource, if needed.

Now, I also created objects in my app that mapped directly to a specific resource. But, when keeping track of a lot of resources, all the extra data pinned to a specific resource just wasn’t needed all the time. A good example of when this situation was true is when I’m keeping track of resources marked as favourite by the user. I stored this data as the simplest object possible, and used an explicit operator to convert the more complicated specific resource object to its simplest form.

If you have any questions about this article, or just want to chat, you can always find me on Twitter @GLanata

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read