使用 LINQ 进行类型转换





5.00/5 (14投票s)
.NET 中的类型转换
引言
.NET 提供了几种在运行时将值类型转换为另一种类型的方法。每种技术都有其局限性。本文提供(又一个)替代方案,当 CLR 能够执行类型转换时,它都适用。
背景
解决类型转换问题的两种流行方法是使用 System.Convert.ChangeType
,或获取 System.ComponentModel.TypeConverter
并调用其 ConvertFrom
方法。第一种方法在尝试将值类型 T
转换为 System.Nullable<T>
时会出错;第二种方法在尝试转换不同的数值类型时会出错,例如,将 float
转换为 double
。这些限制尤其令人沮丧,因为 CLR 具有内置的功能来执行这两种类型的转换。
一种利用这些类型转换功能的方法是构建一个 LINQ lambda 表达式,将其 编译 为 Func<object,object>
,然后在每次需要在这两种类型之间转换时使用编译后的委托。
Using the Code
以下代码实现了这种方法,将其包装到 System.Type
的扩展方法中。
public static class TypeCast {
// This is the method exposing the rest of the functionality
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");
// This is the method with the "guts" of the implementation
[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;
}
// This class provides Equals and GetHashCode
// for a pair of System.Type objects.
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);
}
}
}
现在,您可以像这样使用 Cast
方法
double? x = typeof(double?).Cast(1.0);
int y = typeof(int).Cast(1.2345);
祝您编码愉快!