How to lookup and invoke a .Net TypeConverter for a particular type?

12,617

Solution 1

    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)

Solution 2

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?).

Solution 3

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 (T)Convert.ChangeType(value, typeof(T));
}

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

Solution 4

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.

Share:
12,617
romu
Author by

romu

I'm writing a new book: Rapid Fullstack Development. Learn from my years of experience and become a better developer. My second book, Bootstrapping Microservices, is a practical and project-based guide to building distributed applications with microservices. My first book Data Wrangling with JavaScript is a comprehensive overview of working with data in JavaScript. Data-Forge Notebook is my notebook-style application for data transformation, analysis and transformation in JavaScript. I have a long history in software development with many years in apps, web apps, backends, serious games, simulations and VR. Making technology work for business is what I do: building bespoke software solutions that span multiple platforms. I have years of experience managing development teams, preparing technical strategies and creation of software products. I can explain complicated technology to senior management. I have delivered cutting-edge products in fast-paced and high-pressure environments. I know how to focus and prioritize to get the important things done. Author Rapid Fullstack Development Bootstrapping Microservices Data Wrangling with JavaScript Creator of Market Wizard https://www.market-wizard.com.au/ Creator of Data-Forge and Data-Forge Notebook http://www.data-forge-js.com http://www.data-forge-notebook.com Web www.codecapers.com.au Open source https://github.com/ashleydavis https://github.com/data-forge https://github.com/data-forge-notebook Skills Quickly building MVPs for startups Understanding how to get the most out of technology for business Developing technical strategies Management and coaching of teams &amp; projects Microservices, devops, mobile and fullstack software development

Updated on June 24, 2022

Comments

  • romu
    romu almost 2 years

    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();
    }
    
  • Marc Gravell
    Marc Gravell almost 15 years
    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...
  • Steve Dignan
    Steve Dignan almost 15 years
    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!
  • Marc Gravell
    Marc Gravell almost 15 years
    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...)
  • romu
    romu almost 15 years
    It is interesting to me to see both methods as I intend to use both reflection and TypeConverters.
  • Patrik Svensson
    Patrik Svensson over 12 years
    I don't think Convert.ChangeType uses type converters.
  • Thomas S. Trias
    Thomas S. Trias about 12 years
    Also, look at Enum.ToObject if you need to converter an integral type to an actual enumerated type.
  • jp2code
    jp2code almost 6 years
    @Marc - is there a way to create a method to return the type that I give? Like public T ChangeType<T>(Object item)? Specifically, I need to create a test to see if two (2) Objects are equal, but first I need to establish if they are the same type of object.