Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

The case for a generic C# converter using operators

0.00/5 (No votes)
28 May 2010 1  
Describes how to implement a generic converter between types that takes into account the possible existence of conversion operators.

Introduction

Recently, I encountered a situation where I needed to use a generic converter routine between C# compatible types, but where some of those, but not all, implement either implicit or explicit conversion operators. For this situation, I haven't found a generic solution that could cover all those possible cases.

My situation is that I'm retrieving records from a SQL database, and I have a single parsing routine through which I have to pass all possible fields, regardless of what specific SQL type they have. This routine converts them in real-time to the different objects that implement the application's logic. So, for instance, a given DateTime object got from the SqlDataReader has to be parsed to my Date or Time classes that I use in the application, and that implements its respective conversion operators.

The Solution

The following code implements a generic conversion solution that takes into consideration the possible existence of conversion operators in either the source or target classes. If any of those are found, the code uses them, or otherwise, it tries a last resource mechanism I found while researching in the Internet.

So, firstly, I have declared the two conversion methods in a static class so you can use them with an easy syntax as in TypeEx.ConvertTo(...) or TypeEx.ConvertTo<T>(...), the second method being the generic version of the first one. In both cases, you pass the object to convert into the target type and a specification of this target type.

public static class TypeEx {
  public static object ConvertTo( object source, Type targetType ) ...
  public static T ConvertTo<t>( object source ) ...
}

The first step is to pass some sanity checks, and getting the source type's Type. But in this case, I don't throw an exception, because I wait till I validate that the source object is null, or not, and in the first case, if the target type is of a nullable one:

public static object ConvertTo( object source, Type targetType ) {
  if( targetType == null ) throw new ArgumentNullException
    ( "Target type cannot be null." );
  Type sourceType = source == null ? null : source.GetType();
  // Easy case, when source object is null...
  if( source == null ) {
    if( !IsNullableType( targetType ) )
    throw new InvalidOperationException
    ( "Cannot convert NULL to a non-nullable type '" + 
      targetType + "'." );
    return source;
}

I also made the TypeEx.IsNullableType(...) method a member of the static class, having implemented it in this way:

public static bool IsNullableType( Type type )
{
  if( type == null )
      throw new ArgumentNullException( "Type object cannot be null." );
  if( type.IsValueType ) return false;
  if( type.IsClass ) return true;
  Type generic = type.IsGenericType ? type.GetGenericTypeDefinition() : null;
  bool r = generic == null ? false : generic.Equals( typeof( Nullable<> ) );
  return r;
}

Now, the second step is finding, through Reflection, if an implicit or explicit conversion operator exists where the returning type is the same as the requested target type. If such methods exist, then the code invokes them and returns the result of such invocation:

// Trying conversion operators...
MethodInfo[] sourceinfos = sourceType.GetMethods
    ( BindingFlags.Public | BindingFlags.Static );
foreach( MethodInfo info in sourceinfos ) {
  if( ( info.Name == "op_Explicit" || info.Name == "op_Implicit" )
    && info.ReturnType == targetType ) {
    object r = info.Invoke( source, new[] { source } );
    return r;
  }
}

MethodInfo[] targetinfos = targetType.GetMethods
    ( BindingFlags.Public | BindingFlags.Static );
foreach( MethodInfo info in targetinfos ) {
  if( ( info.Name == "op_Explicit" || info.Name == "op_Implicit" )
    && info.ReturnType == targetType ) {
    object r = info.Invoke( source, new[] { source } );
    return r;
  }
}

Finally, no conversion operators were implemented neither in the source nor target classes. As a last resort case, I use the following code that tries to force a conversion to the underlying target type:

// As last resource...
if( targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals
    ( typeof( Nullable<> ) ) ) {
  // If it is a nullable type and not null,
  // it can be converted to its underlying type...
  NullableConverter nc = new NullableConverter( targetType );
  targetType = nc.UnderlyingType;
}
return Convert.ChangeType( source, targetType );

Conclusion

In many circumstances, when you implement your own classes, you also implement their conversion operators. The code I present here takes into consideration when those operators exist to use them for a generic conversion routine you can use with confidence. It will convert among standard CLR types as well.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here