Introduction
.NET provides several ways to change the type of a value to another type at run-time. Each technique has its limitations. This article provides (yet) another alternative that works in all instances when the CLR is capable of performing a type cast.
Background
Two popular solutions to the problem of type conversion are to use System.Convert.ChangeType
, or to obtain System.ComponentModel.TypeConverter
and call its ConvertFrom
method. The first method breaks when you try converting a value type T
to System.Nullable<T>
; the second one breaks when you try converting different numeric types, for example, float
to double
. These limitations appear especially frustrating, because the CLR has built-in capabilities to perform both types of conversion.
One way of using these type casting capabilities is to build a LINQ lambda expression, compile it into a Func<object,object>
, and then use the compiled delegate every time we need to convert between two types.
Using the Code
The code below implements this approach, wrapping it into an extension method of System.Type
.
public static class TypeCast {
public static object Cast(this Type type, object obj) {
return GetConverter(type, obj)(obj);
}
private static readonly IDictionary<PairOfTypes,Func<object,object>> converters =
new Dictionary<PairOfTypes,Func<object,object>>();
private static readonly ParameterExpression convParameter =
Expression.Parameter(typeof(object), "val");
[MethodImpl(MethodImplOptions.Synchronized)]
private static Func<object,object> GetConverter(Type targetType, object val) {
var fromType = val != null ? val.GetType() : typeof(object);
var key = new PairOfTypes(fromType, targetType);
Func<object,object> res;
if (converters.TryGetValue(key, out res)) {
return res;
}
res = (Func<object,object>)Expression.Lambda(
Expression.Convert(
Expression.Convert(
Expression.Convert(
convParameter
, fromType
)
, targetType
)
, typeof(object)
)
, convParameter
).Compile();
converters.Add(key, res);
return res;
}
private class PairOfTypes {
private readonly Type first;
private readonly Type second;
public PairOfTypes(Type first, Type second) {
this.first = first;
this.second = second;
}
public override int GetHashCode() {
return 31*first.GetHashCode() + second.GetHashCode();
}
public override bool Equals(object obj) {
if (obj == this) {
return true;
}
var other = obj as PairOfTypes;
if (other == null) {
return false;
}
return first.Equals(other.first)
&& second.Equals(other.second);
}
}
}
Now, you can use the Cast
method like this:
double? x = typeof(double?).Cast(1.0);
int y = typeof(int).Cast(1.2345);
Happy coding!