共3条
1/1 1 跳转至页
Linq的超越——强类型反射
大家都知道Linq引入了标准查询操作符,从而使查询成为C#语言中最重要的概念。但不知您是否意识到,Linq还可用于查询外的其他用途。下面我将首次探讨Linq用于查询外的其他领域。
反射问题:远离类型安全错误
至少就我自己而言,使用C#这种类型安全语言时,每当按下Ctrl+Shift+B(或Shift+F6)键进行编译时,会有一种轻松和放心的感觉。我知道,由于使用错误的类型而产生的怪异且难于调试的运行时错误,以及像“方法缺失”这种提示几乎从未出现。但是使用反射时,如果我不小心,就会出现臭名昭著的TargetInvocationException和一些NullReferenceException之类的异常。下面正是我所遇到的
MethodInfo method =
typeof(Mock).GetMethod("PublicMethodParameters",
newType[]{ typeof(string), typeof(int) }));
若对Mock类应用重构以便重命名该方法,会发生什么情况呢?如果参数类型发生更改,会发生什么情况呢?毫无疑问,会发生运行时异常!如果采用一个使用大量反射的插入式灵活框架,这决不是一个小问题。由于害怕出错而不敢应用重构(或者使其代价昂贵)必然会限制您改进设计和完善代码的能力。那么,试着替换魔力字符串(magic strings)和松散类型的Type数组将会如何呢?
MethodInfo info =
Reflector.Method(
(x, y, z) => x.PublicMethodParameters(y, z));
通过Linq进行强类型反射
其工作原理是作为参数传递的λ表达式(就像前一版本.NET中的委托一样)不一定要执行。
上面的代码基本上构造了一个可以调用类型上给定方法的λ表达式。声明方法的目标类型的类型就是Method<> static generic方法的第一个类型参数。您可指定的可选类型参数将是您要调用的方法的参数类型(如果存在)。如果我想获得无参数方法的MethodInfo,则表达式将是:MethodInfo info = Reflector.Method(
x => x.PublicMethodNoParameters());
这比您以前见到的任何λ表达式都典型。在λ表达式中,如果您需要传递附加参数,则必须将所有内容放到括号中(上例中的x、y和z)。用于属性和字段的类型映射功能是相同的:
PropertyInfo property =
Reflector.Property(x => x.PublicProperty);
FieldInfo field =
Reflector.Field(x => x.PublicField);
利用表达式树
现在我们开始讨论比较有趣的内容,即如何实现它。
在Linq中,任何接收λ表达式(委托类型)的方法都可以转换为接收相同委托类型的Expression的方法,并且不需要更改客户机代码。例如:
privatestaticvoid DoSomething(Predicate predicate)
可以替换为:
privatestaticvoid DoSomething(
Expression> predicate)
在上述两种情况下,调用代码可以是相同的λ表达式:
DoSomething(x => x.Value > 25);
这里发生的情况是,编译器不会将指针传入到第二个方法签名的匿名委托中,而是生成以表达式树的形式构建AST(抽象语法树)的IL代码。如果您打开Reflector(我的类型反射类的名字也由此而来,它是任何高级开发人员都应该经常使用的最伟大的工具)并取消对DoSomething的方法调用,就可以看到:
ParameterExpression expression1 =
Expression.Parameter(typeof(Mock), "x");
Program.DoSomething(
Expression.Lambda>(
Expression.GT(Expression.Field(
expression1, fieldof(Mock.Value)),
Expression.Constant(0x19, typeof(int))),
newParameterExpression[]{expression1 })
);
关键词: 超越 类型 反射 方法
这里您可以看到编译器如何使用Expression类上的静态方法构建整个表达式(我对API的详细看法另外单独讨论)。当然,在方法实现中,您可以检查相同的树并执行任何想执行的操作。最新的Linq CTP包含一个非常酷的可视化工具,在运行时到达您的方法主体时可以用来查看表达式树中的情况:
到现在为止,您应该明白了我正在实现一个强类型反射:我接收一个表达式树,并在其中搜索方法调用节点(或者,对于属性和字段来说是成员访问)。下面是Method<>方法的实现
publicstaticMethodInfo Method<TDeclaringType>(
Expression<Operation> method) { return GetMethodInfo(method); } privatestaticMethodInfo GetMethodInfo(Expression method) { LambdaExpression lambda = method asLambdaExpression; if (lambda == null)
thrownewArgumentNullException("method"); MethodCallExpression methodExpr = null; // 我们的Operation<T>返回一个对象,故首先可以声名一
// 个类型转换(如果方法无返回对象)或直接方法调用。 if (lambda.Body.NodeType == ExpressionType.Cast) { // 类型转换是一个一元操作,而操作数是一个方法调用表达式。 methodExpr = ((UnaryExpression)lambda.Body).
Operand asMethodCallExpression; } elseif (lambda.Body.NodeType == ExpressionType.MethodCall || lambda.Body.NodeType == ExpressionType.MethodCallVirtual) { methodExpr = lambda.Body asMethodCallExpression; } if (methodExpr == null)
thrownewArgumentException("method"); return methodExpr.Method; }我创建的就是Operation委托类型。不能使用Linq Func<T>(以及T、Arg0……),因为它们返回的是布尔值。我需要更灵活的对象,简单来说就是返回对象的对象,以及接收一些固定参数类型(例如Func<T>)的委托“重载”。因此我得到如下内容: publicdelegateobjectOperation(); publicdelegateobjectOperation<T>(T declaringType); publicdelegateobjectOperation(T declaringType, A0 arg0); ... 注意,API的用户从来都不会知道这些委托类型的对象的存在,就像查询操作符的用户从不知道Func<T>的存在一样。我希望将来这些委托能够消失,而代之以更好的东西(可能是publicdelegateobject Operation < params T> ;))。此外,注意我是如何将新的参数类型的参数添加到T“后面”的,T是重载的通用转换,与Linq在Func<T>中的功能正好相反。 属性和字段与上面的例子非常类似,没有什么特殊之处。不过,Method重载确实是一个很好的部分: publicstaticMethodInfo Method<TDeclaringType>(
Expression<Operation<TDeclaringType>> method) { return GetMethodInfo(method); } publicstaticMethodInfo Method<TDeclaringType, A0>(
Expression<Operation<TDeclaringType, A0>> method) { return GetMethodInfo(method); } publicstaticMethodInfo Method<TDeclaringType, A0, A1>(
Expression<Operation<TDeclaringType, A0, A1>> method) { return GetMethodInfo(method); } ...
Expression<Operation> method) { return GetMethodInfo(method); } privatestaticMethodInfo GetMethodInfo(Expression method) { LambdaExpression lambda = method asLambdaExpression; if (lambda == null)
thrownewArgumentNullException("method"); MethodCallExpression methodExpr = null; // 我们的Operation<T>返回一个对象,故首先可以声名一
// 个类型转换(如果方法无返回对象)或直接方法调用。 if (lambda.Body.NodeType == ExpressionType.Cast) { // 类型转换是一个一元操作,而操作数是一个方法调用表达式。 methodExpr = ((UnaryExpression)lambda.Body).
Operand asMethodCallExpression; } elseif (lambda.Body.NodeType == ExpressionType.MethodCall || lambda.Body.NodeType == ExpressionType.MethodCallVirtual) { methodExpr = lambda.Body asMethodCallExpression; } if (methodExpr == null)
thrownewArgumentException("method"); return methodExpr.Method; }我创建的就是Operation委托类型。不能使用Linq Func<T>(以及T、Arg0……),因为它们返回的是布尔值。我需要更灵活的对象,简单来说就是返回对象的对象,以及接收一些固定参数类型(例如Func<T>)的委托“重载”。因此我得到如下内容: publicdelegateobjectOperation(); publicdelegateobjectOperation<T>(T declaringType); publicdelegateobjectOperation(T declaringType, A0 arg0); ... 注意,API的用户从来都不会知道这些委托类型的对象的存在,就像查询操作符的用户从不知道Func<T>的存在一样。我希望将来这些委托能够消失,而代之以更好的东西(可能是publicdelegateobject Operation < params T> ;))。此外,注意我是如何将新的参数类型的参数添加到T“后面”的,T是重载的通用转换,与Linq在Func<T>中的功能正好相反。 属性和字段与上面的例子非常类似,没有什么特殊之处。不过,Method重载确实是一个很好的部分: publicstaticMethodInfo Method<TDeclaringType>(
Expression<Operation<TDeclaringType>> method) { return GetMethodInfo(method); } publicstaticMethodInfo Method<TDeclaringType, A0>(
Expression<Operation<TDeclaringType, A0>> method) { return GetMethodInfo(method); } publicstaticMethodInfo Method<TDeclaringType, A0, A1>(
Expression<Operation<TDeclaringType, A0, A1>> method) { return GetMethodInfo(method); } ...
不知道为什么,这个铁自发出来后有一些不知道怎么跑出来的单词,大家感兴趣的话可以到这看http://blog.csdn.net/programmer_editor/archive/2006/09/29/1305859.aspx
共3条
1/1 1 跳转至页
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |