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.
TypeConverter is a class in .Net so meaby you should change title to something less confusing.
This article is interesting anyway.
Best Regards.
Nice approach. Altough i would rather call it ‘transparent’ than ‘universal’. Because the idea is to make the object’s real type transparent for the user. While if it was universal, it would support any type out of the box. Which is not the case.
Regards!
Philippe
Interesting, but I’m not sure how useful it is. Just imagine having to remember the type of each property – a nightmare if used in a larger application. What’s worse, if you should get a type wrong somewhere there’s no exception thrown, instead a default value is returned. Looks suspiciously like an anti-pattern to me.
Hello. I was just looking info about type converters. I have readen your article. After this I saw the first comment and wanted to say this guy is right. Until I saw it’s name. LOL.
Btw. I have implemented something that i named UniversalTypeConverter in my code, but it’s seriously something different.
What you are doing is type conversion. Not Type Converter.
Update: We are switching obfuscators from Xenocode to Eziriz .NET Reactor. Article and obfuscator review coming soon…