Wednesday, March 6, 2013

Conversion Operators Pros and Cons

C# enables programmers to declare conversions on classes so they can be converted to and/or from other types. These conversions can be explicit or implicit.

Below is an example from Microsoft:
class SampleClass
{
    public static explicit operator SampleClass(int i)
    {
        SampleClass temp = new SampleClass();
        // code to convert from int to SampleClass...

        return temp;
    }
}
Conversion operators are a great way of simplifying business logic, however they have some important limitations. One of these is that the operator may only have one parameter (the source type). This means that the only values available when the target type is initialized are those provided by the source type and dependency injection cannot be used. It's also worth noting that these conversions will not chain automatically. Declaring conversions from A->B and B->C does not provide a way to directly convert A->C.

This is all fine in theory, but what I find useful in these types of articles are real world examples, so here it is.

We're using Microsoft Service Bus for Windows to pass events between an e-commerce solution based on nopCommerce and a custom back office application. We needed some basic objects to pass relevant information through the bus and did not want to be too tightly coupled to the nopCommerce domain objects. My initial solution was to create classes and implement conversion operators to cast objects from nopCommerce to their POCO counterparts.

In the example below, you can see an implicit conversion operator for the Order class which uses the Shipment conversion operator.
public static implicit operator Order(Core.Domain.Orders.Order o)
{
  return new Order
  {
    OrderId = o.Id,
    Shipments = o.Shipments.Select(x => (Shipment)x)
  };
}
Since we needed to add more information to our Order object than was contained in the version from nopCommerce, I got rid of the conversion operator and created a separate conversion class with the following method:
public Order ToOrder(Core.Domain.Orders.Order o)
{
  return new Order
  {
    OrderId = o.Id,
    Shipments = o.Shipments.Select(ToShipment).ToList()
  };
}
The method contents are nearly identical, however we are now free to inject any dependencies we like in the conversion class constructor and add parameters to the conversion method.

No matter which approach you choose, these are excellent ways to simplify conversion and consolidate code used for creating related types. If you have any suggestions or feedback on this article, please leave a comment below or contact me directly.