当类型转换表达式遇上自定义转换操作
2010-10-15 13:50 by 老赵, 2987 visits之前我提到说System.Json是一个十分不好用的类库,其中一点就是在于,我没法将一个JsonValue转化为范型类型——它只为Int32,String等几种特定类型定义了隐式转换,又无法得到以object类型所引用的值。不过这也难不到拥有“在运行时创建自定义表达式树并编译成动态代码”的.NET程序员。例如我们可以写这样一个辅助类进行JsonValue至任意类型的转化操作,.NET类库会负责为我们选择合适的转换方式:
class JsonConverter<T> { static JsonConverter() { var jsonValueExpr = Expression.Parameter(typeof(JsonValue), "jsonValue"); var convertExpr = Expression.Convert(jsonValueExpr, typeof(T)); var lambdaExpr = Expression.Lambda<Func<JsonValue, T>>(convertExpr, jsonValueExpr); s_convert = lambdaExpr.Compile(); } private static Func<JsonValue, T> s_convert; public static T Convert(JsonValue jsonValue) { return s_convert(jsonValue); } }
泛型参数字典和动态代码生成都是性能的有效保证,而构造一颗我们需要的表达式树也十分容易。这个方法我以前也谈起过,我认为每个称职的.NET程序员都应该熟练掌握这种“通用操作”的实现方式。不过我最近在构造这种类型转换表达式的时候遇到了一些问题。例如,如果我们需要构造一个表达式,将一个JsonPrimitive(而不是先前的JsonValue)转化为int,这便会抛出一个异常,提示我们“没有合适的强制转化方式”。
我猜,可能是因为那些隐式转化操作定义在JsonValue上吧。那么这算不算一个Bug?我认为是,因为与它等价的代码是没有任何问题的,例如:
var value = (int)(new JsonPrimitive(10));
C#编译器会为我们找到JsonValue上定义的op_Implict方法并生成调用代码,而.NET类库却不会这么做,双方配合不够默契。不过其实这很容易绕开,只是我们要将这样的代码:
var convertExpr = Expression.Convert(instanceExpr, targetType);
修改为:
static Expression GetConvertExpression(Expression instanceExpr, Type targetType) { var mediateType = instanceExpr.Type; if (mediateType == typeof(object)) { // (TargetType)instance return Expression.Convert(instanceExpr, targetType); } while (mediateType != typeof(object)) { try { // (MediateType)instace var mediateExpr = Expression.Convert(instanceExpr, mediateType); // (TargetType)(MediateType)instance return Expression.Convert(mediateExpr, targetType); } catch { mediateType = mediateType.BaseType; } } throw new Exception(...); }
我们选择的策略很简单,提供一个“过渡类型”,先将instance转换为过渡类型,再转换成目标类型。如果发生转换错误,则会继续尝试其父类,直至object类型为止,则抛出异常表示转换失败。目前看来这个做法并没有什么问题。
有点绕,为什么我没有用到这些呢?使用场景在哪里呢