Problem
When you create a generic method (Foo<T>) or generic class (Bar<T>), it often happens that you need to change (or convert) a type to T. In these situations you need a ChangeType<T> function that changes "any" type to T. Unfortunately this is not a standard method. The Cast() method of Linq looks like a solution but it is very limited. This wil not work for a lot of types:
public static T ChangeTypeLinqVariant<T>(this object value)
{
return (new [] {value}).Cast<T>().Single();
}
Examples
Most obvious examples where you need a ChangeType<T> method are:
- Method returns T but the parameter of the method is an object, string, XML or data from a DataReader.
- Method returns T1 but the parameters of the method is T2 (T1 Foo<T1, T2>(T2 value))
A less obvious example is:
private void AddItem<TK, TV>(ref Dictionary<TK, TV> dictionary, TK key, TV value)
{
if (key is string)
{
// This wil not compile because key is a TK, not a string
key = key.ToString().ToLower();
// This will do the job
key = key.ToString().ToLower().ChangeType<TK>();
}
if (dictionary.ContainsKey(key)) return;
dictionary.Add(key, value);
}
Solution
A start it the good direction was a post on StackOverflow. But that solution didn't work for types like enum's, strings, Guids, etc. So I add soms new code to it and now it works for most of the common scenarios.
public static T ChangeType<T>(this object value, CultureInfo cultureInfo)
{
var toType = typeof(T);
if (value == null) return default(T);
if (value is string)
{
if (toType == typeof(Guid))
{
return ChangeType<T>(new Guid(Convert.ToString(value, cultureInfo)), cultureInfo);
}
if ((string)value == string.Empty && toType != typeof(string))
{
return ChangeType<T>(null, cultureInfo);
}
}
else
{
if (typeof(T) == typeof(string))
{
return ChangeType<T>(Convert.ToString(value, cultureInfo), cultureInfo);
}
}
if (toType.IsGenericType &&
toType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
toType = Nullable.GetUnderlyingType(toType); ;
}
bool canConvert = toType is IConvertible || (toType.IsValueType && !toType.IsEnum);
if (canConvert)
{
return (T)Convert.ChangeType(value, toType, cultureInfo);
}
return (T)value;
}
It might be handy to add some extra overloads like:
public static T ChangeType<T>(this object value)
{
return ChangeType<T>(value, CultureInfo.CurrentCulture);
}