views:

829

answers:

4

I would like to implement a general purpose runtime type conversion function that makes use .Net TypeConverters to do the conversion.

Does anyone know how to how to look up and invoke a TypeConverter for a particular type?

Consider this C# example:

//
// Convert obj to the type specified by 'toType'.
// 
object ConvertTo(object obj, Type toType)
{
    if (TypeIsEqualOrDerivesFrom(obj.GetType(), toType)) <-- I know how to implement this.
    {
        // The type of obj is the same as the type specified by 'toType' or
        // the type of obj derives from the type specified by 'toType'.
        return obj;
    }

    if (TypeConverterExists(obj.GetType(), toType) <-- How do I implement this?
    {
        // There exists a type convertor that is capable of converting from 
        // the type of obj to the type specified by 'toType'.
        return InvokeTypeConverter(obj, toType); <-- How do I implement this?
    }

    throw new TypeConversionFailedException();
}
+6  A: 
    TypeConverter converter = TypeDescriptor.GetConverter(sourceType);
    if(converter.CanConvertTo(destinationType)) {
        return converter.ConvertTo(obj, destinationType);   
    }
    converter = TypeDescriptor.GetConverter(destinationType);
    if(converter.CanConvertFrom(sourceType)) {
        return converter.ConvertFrom(obj);
    }

You could also look at Convert.ChangeType(obj, destinationType)

Marc Gravell
A: 

In addition to Marc's answer, you might consider that ASP.NET's ViewState does a similar function to what you're asking to do. It tries to find the most efficient conversion.

It might be worth a look at this page and possibly this one too.

Jeff Moser
+2  A: 

Here's the code used in one of our existing apps... not sure if it's ideal but it works well for us.

/// <summary>
/// Attempts to convert a value using any customer TypeConverters applied to the member
/// </summary>
/// <param name="value">Object containing the value to be assigned</param>
/// <param name="member">Member to be assigned to</param>
/// <returns><paramref name="value"/> converted to the appropriate type</returns>
public static Object CustomTypeConversion ( object value, MemberInfo member )
{
    if ( value == null || value == DBNull.Value )
        return value;

    if ( member == null )
        throw new ArgumentNullException ( );

    List<TypeConverter> converters = GetCustomTypeConverters ( member );

    foreach ( TypeConverter c in converters )
    {
        if ( c.CanConvertFrom ( value.GetType ( ) ) )
            return c.ConvertFrom ( value );
    }

    if ( member is PropertyInfo )
    {
        PropertyInfo prop = member as PropertyInfo;
        return ConvertToNative( value , prop.PropertyType );
    }
    return ConvertToNative ( value, member.MemberType.GetType ( ) );
}

/// <summary>
/// Extracts and instantiates any customer type converters assigned to a 
/// derivitive of the <see cref="System.Reflection.MemberInfo"/> property
/// </summary>
/// <param name="member">Any class deriving from MemberInfo</param>
/// <returns>A list of customer type converters, empty if none found</returns>
public static List<TypeConverter> GetCustomTypeConverters ( System.Reflection.MemberInfo member )
{
    List<TypeConverter> result = new List<TypeConverter>();

    try
    {
        foreach ( TypeConverterAttribute a in member.GetCustomAttributes( typeof( TypeConverterAttribute ) , true ) )
        {
            TypeConverter converter = Activator.CreateInstance( Type.GetType( a.ConverterTypeName ) ) as TypeConverter;

            if ( converter != null )
                result.Add( converter );
        }
    }
    catch
    {
        // Let it go, there were no custom converters
    }

    return result;
}

/// <summary>
/// Attempts to cast the incoming database field to the property type
/// </summary>
/// <param name="value">Database value to cast</param>
/// <param name="castTo">Type to cast to</param>
/// <returns>The converted value, if conversion failed the original value will be returned</returns>
public static object ConvertToNative ( object value , Type castTo )
{
    try
    {
        return Convert.ChangeType( value , castTo , System.Threading.Thread.CurrentThread.CurrentCulture );
    }
    catch
    {
        return value;
    }
}

Just call the CustomTypeConversion method and away you go... probably a little more than you need but being thorough isn't a crime (or is it?).

Steve Dignan
Note that TypeConverter implementations can be added at runtime (via TypeDescriptor) - and the standard framework will pick that up - but reflection (above) *won't* pick up such additions; System.ComponentModel is designed to be very flexible at runtime...
Marc Gravell
Ah, good one... that issue has come up for us (yet) -- this code has been around our app for a while; it's good to have a fresh set of eyes review it. Thanks!
Steve Dignan
To be honest, in most cases you wouldn't ever see the difference... it isn't *that* common that people start adding runtime converters/descriptors. But it is possible (I've got an example on stackoverflow somewhere...)
Marc Gravell
It is interesting to me to see both methods as I intend to use both reflection and TypeConverters.
Ashley Davis
A: 

I don't know how robust it is, but I sometimes use this code for generic type conversion :

public T ConvertTo<T>(object value)
{
    return Convert.ChangeType(value, typeof(T));
}

I don't know if the ChangeType method use TypeConverters...

Thomas Levesque