Click here to Skip to main content
Licence CPOL
First Posted 27 May 2010
Views 11,314
Bookmarked 24 times

The case for a generic C# converter using operators

By | 28 May 2010 | Article
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Moises Barba



Spain Spain

Member

Moises Barba works for the Consulting division of a major multinational IT company.
While he has not to develop for a living nowadays, solving complex puzzles has been ever among his main interests - that's why he has spent his latest 20 years trying to combine his degree is in Theoretical Physics with his MBA... and he is still trying to figure how this two things can fit together.
Flying a lot across many countries, along with the long working days that are customary in consultancy, he can say that, after all, he lives in Spain (at least the weekends).

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
General.NET 3.5 PinmemberRichard Deeming4:01 2 Jun '10  
GeneralRe: .NET 3.5 Pinmembermbarbac0:46 10 Jun '10  
GeneralRe: .NET 3.5 PinmemberRichard Deeming5:33 10 Jun '10  
GeneralRe: .NET 3.5 Pinmembermbarbac9:00 13 Aug '10  
GeneralComment and Question PinmemberJohn Simmons / outlaw programmer5:02 28 May '10  
GeneralRe: Comment and Question Pinmembermbarbac8:37 29 May '10  
GeneralReal-time? PinmemberArchAngel12315:23 31 May '10  
GeneralNice PinmemberMatt McKinney4:29 28 May '10  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 28 May 2010
Article Copyright 2010 by Moises Barba
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid