I would go with the TypeConverter stuff. It's basically a class that does conversions to/from values and cultures. The primary difference between a TypeConverter and Convert.ChangeType is that the later requires the IConvertible interface on the source type, while TypeConverters can work with any objects.
I've created a helper class for this, since I often store different configuration objects in xml-files. That's also why it's hardcoded to convert to/from CultureInfo.InvariantCulture.
public static class TypeConversion {
public static Object Convert(Object source, Type targetType) {
var sourceType = source.GetType();
if (targetType.IsAssignableFrom(sourceType))
return source;
var sourceConverter = TypeDescriptor.GetConverter(source);
if (sourceConverter.CanConvertTo(targetType))
return sourceConverter.ConvertTo(null, CultureInfo.InvariantCulture, source, targetType);
var targetConverter = TypeDescriptor.GetConverter(targetType);
if (targetConverter.CanConvertFrom(sourceType))
return targetConverter.ConvertFrom(null, CultureInfo.InvariantCulture, source);
throw new ArgumentException("Neither the source nor the target has a TypeConverter that supports the requested conversion.");
}
public static TTarget Convert<TTarget>(object source) {
return (TTarget)Convert(source, typeof(TTarget));
}
}
It's fully possible to create your own TypeConverter to handle system types, like System.Version (which doesnt implement IConvertible) to support conversions like from strings containing a version number ("a.b.c.d") to an actual Version object.
public class VersionTypeConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
var s = value as string;
if (s != null)
return new Version(s);
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
var v = value as Version;
if (v != null && destinationType == typeof(string)) {
return v.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
To actually use this provider you need to registered it during application startup, using TypeDescriptor.AddProvider, passing in a custom TypeDescriptionProvider, and typeof(Version). This needs to return a custom CustomTypeDescriptor in the TypeDescriptorProvider.GetTypeDescriptor method, and the descriptor needs to override GetConverter to return a new instance of VersionTypeConverter. Easy. ;)