Sometimes you need to be able to convert between multiple data types. One solution is to create a “universal type converter,” which is an object that implicitly executes all of the desired type conversions.

Example

The best way to illustrate this is with an example. Assume we have a simple object class “Obj” that can contain multiple dynamic properties, defined by the “Prop” class. (By “dynamic” I mean the properties can be created at run-time, rather than at compile time as is typical with C# properties). Each dynamic property has a “Name” and “Value” and is stored in a Dictionary indexed by the property’s Name. The “GetProp” method returns an existing property or creates a new property if needed. For simplicity, error checking is omitted and public fields are used.

public class Obj
{
    private Dictionary<string, Prop> m_Props =
        new Dictionary<string, Prop>();
    public Prop GetProp( string propName )
    {
        Prop prop = null;
        if (!this.m_Props.TryGetValue( propName, out prop ))
        {
            prop = new Prop();
            prop.Name = propName;
            this.m_Props.Add( propName, prop );
        }
        return prop;
    }
}
public class Prop
{
    public string Name;
    public object Value;
}

Using a Universal Type Converter

Let’s say we want to get and set the dynamic property values with an easy syntax–using the object’s index operator []. For example, consider this console program that creates two of our objects, sets their dynamic property values, then gets and writes the property values to the console window:

class Program
{
    static void Main( string[] args )
    {
        Obj obj = new Obj();

        obj["Name"] = "Joe";
        obj["Age"] = 27;
        obj["StartDate"] = new DateTime( 2002, 9, 8 );
        Write( obj );

        obj["Name"] = "Mike";
        obj["Age"] = 34;
        obj["StartDate"] = new DateTime( 2005, 12, 5 );
        Write( obj );

        Console.ReadLine();
    }
    static void Write( Obj obj )
    {
        string name = obj["Name"];
        int age = obj["Age"];
        DateTime startDate = obj["StartDate"];

        Console.WriteLine( "name={0} age={1} start={2:d}",
            name, age, startDate );
    }
}

How would you accomplish this? It’s not as easy as it looks. Fire up a Visual Studio console project and test your C# skills. Then return here to see how you can solve this challenge with a universal type converter.

(waiting…)

Building a Universal Type Converter

Welcome back (if you left). How did you do?

For our universal type converter (UTC) we define the “PropValue” class, which represents a property value. This class converts any of our supported types into a property value and vice-versa. To keep this example simple, we will only support values that have a DateTime, integer or string type, but naturally you could extend this UTC to include any native C# types or other types that you have defined. Here is the full class:

public class PropValue
{
    #region CONSTRUCTORS
    public PropValue( object value )
    {
        this.m_Value = value;
    }
    public PropValue( Obj obj, string propName )
    {
        this.m_Obj = obj;
        this.m_PropName = propName;
    }
    #endregion CONSTRUCTORS

    #region VALUE
    private object m_Value;
    public object Value
    {
        get
        {
            return this.m_Value;
        }
    }
    #endregion VALUE

    #region PROPERTY
    private string m_PropName;
    private Obj m_Obj;
    private Prop GetProp()
    {
        return this.m_Obj.GetProp( this.m_PropName );
    }
    #endregion PROPERTY

    #region CONVERTERS
    #region FROM VALUE
    public static implicit operator DateTime( PropValue propVal )
    {
        object value = propVal.GetProp().Value;
        return value is DateTime ?
            (DateTime)value : DateTime.MinValue;
    }
    public static implicit operator int( PropValue propVal )
    {
        object value = propVal.GetProp().Value;
        return value is int ?
            (int)value : 0;
    }
    public static implicit operator string( PropValue propVal )
    {
        object value = propVal.GetProp().Value;
        return value is string ?
            (string)value : null;
    }
    #endregion FROM VALUE

    #region TO VALUE
    public static implicit operator PropValue( DateTime val )
    {
        return new PropValue( val );
    }
    public static implicit operator PropValue( int val )
    {
        return new PropValue( val );
    }
    public static implicit operator PropValue( string val )
    {
        return new PropValue( val );
    }
    #endregion TO VALUE
    #endregion CONVERTERS
}

Notice the conversion operators that go both ways for each type: PropValue <–> DateTime, PropValue <–> integer, and PropValue <–> string. Also notice the two constructors: one that accepts a value, another that accepts the Obj and Prop name that identifies the property.

Of course, we also need to add the index operator [] to the Obj class. The get-block returns the value in its native type. The set-block finds the appropriate Prop dynamic property and sets its Value:

public PropValue this[string propName]
{
    get
    {
        return new PropValue( this, propName );
    }
    set
    {
        Prop prop = this.GetProp( propName );
        prop.Value = value.Value;
    }
}

Note that you can improve efficiency by moving the UTC inside the Prop class. This eliminates the need to create a PropValue object each time you get or set a property value. However, there may be times you do not want the Prop class itself to have UTC capability, in which case a separate converter class is necessary, as shown in this example.