Feb 23, 2009

Type Conversion

I ran into a bit of confusing code recently while I was trolling through some code.  Here’s a simple example.

What Not To Do

This code had two classes that were very similar, but in terms of inheritance were not directly related.  Here I have a similar setup with two classes, Mammal and Table.

ClassDiagram1

Notice that they have identical properties, but you could safely say they had nothing to do with each other.  What’s why I found it odd that the code author was somehow able to get away with this:

Mammal dog = new Mammal();
dog.Color = "Brown";
dog.NumberOfLegs = 4;

//What??
Table dogTable = dog;

Now, I’ve seen some strange stuff in my time, but never have I seen someone get away from that assignment.  That shouldn’t even compile.

I looked into it further and the programmer had done something clever… a little too clever.

public static implicit operator Table(Mammal m)
{
  Table t = new Table();
  t.Color = m.Color;
  t.NumberOfLegs = m.NumberOfLegs;
  return t;
}

The programmer had overloaded the implicit cast operator for that type to allow implicit casting between the two types instead of writing some sort of helper method for conversion.

Several things are wrong with this approach, but the most obvious is the violation of the Principle of Least Surprise.  Kudos to the programmer for trying to simplify code, though.

What To Do

It’s fairly common to need to adapt from one type to another at runtime.  It’s very common these days to need to convert a data entity from a data store to a type that can be served from a service interface, for example.  There are quite a few ways to skin this particular cat.  I’ll show you two I use commonly.

The simple solution is to use a static adaptation method.  There are several advantages to this.  For one, it’s simple to understand.

public static class Converter
{
  public static Table ConvertToTable(Mammal m)
  {
      Table t = new Table();
      t.Color = m.Color;
      t.NumberOfLegs = m.NumberOfLegs;
      return t;
  }
}

You also isolate the coupling between these two types outside of the classes themselves.

The other thing that’s great about this is that it’s compatible with quite a few other scenarios, like converting a list of one entity to another.

Mammal[] mammalList = GetAllMammals();
Table[] tables = Array.ConvertAll(mammalList,
                                 m => Converter.ConvertToTable(m));

AutoMapper

Another, more elegant solution is a library called AutoMapper which comes courtesy of Jimmy Bogard (Twitter). Without writing any explicit conversion code, AutoMapper allows you to do things like this:

Mammal dog = new Mammal() { Color = "Brown", NumberOfLegs = 4 };
Table table = new Table();

//Setup code.  You only have to do this once.
//Maps are cached statically.
AutoMapper.Mapper.CreateMap<Mammal, Table>();

table = AutoMapper.Mapper.Map(dog, table);

Which is brilliant.  Converting an array of objects is just as easy.  I highly recommend this very flexible library.

Hopefully this gives you a few strategies for not confusing me and making your life easier.

No comments:

Post a Comment