《程序员》官方BLOG

欢迎热心的读者随时与我们交流,您的支持是我们最大的动力!

用户操作
[即时聊天] [发私信] [加为好友]
programmer_editorID:programmer_editor
1044897次访问,排名24好友0人,关注者69
programmer_editor的文章
原创 186 篇
翻译 1 篇
转载 3 篇
评论 1360 篇
最近评论
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
apple_hufangqiaqia:教我们.net的老师也讲这本书蛮好的
cyferl:读完,了解到不少网路安全方面的知识!
zhouqd:很想了解软件工程的历史
elvishehai:呵呵
文章分类
收藏
    相册
    《程序员》08年封面秀
    06年《程序员》封面秀
    07年《程序员》封面秀
    Logo
    编辑部合影
    调查分析图表
    模版
    文章用图
    新年祝福,名家寄语
    友情链接
    博文视点
    杂志订阅
    杂志订阅
    征文启事
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    翻译 Linq的超越——强类型反射收藏

    新一篇: World Cup——世界杯的另类读法 | 旧一篇: 西摩•克雷(Seymour Cray)――隐居丛林的超级计算机之父

     

    Linq的超越
    ——强类型反射
    文/Daniel Cazzulino
     
    大家都知道Linq引入了标准查询操作符,从而使查询成为C#语言中最重要的概念。但不知您是否意识到,Linq还可用于查询外的其他用途。下面我将首次探讨Linq用于查询外的其他领域。
    反射问题:远离类型安全错误
    至少就我自己而言使用C#这种类型安全语言时,每当按下Ctrl+Shift+BShift+F6进行编译时会有一种轻松和放心的感觉。我知道,由于使用错误的类型而产生的怪异且难于调试的运行时错误,以及像“方法缺失”这种提示几乎从未出现。但是使用反射时,如果我不小心,就会出现臭名昭著的TargetInvocationException和一些NullReferenceException之类的异常。下面正是我所遇到的
    MethodInfo method =
    typeof
    (Mock).GetMethod("PublicMethodParameters",
        newType[]{ typeof(string), typeof(int) }));
    如果对Mock类应用重构以便重命名该方法会发生什么情况呢如果参数类型发生更改会发生什么情况呢?毫无疑问,会发生运行时异常!如果采用一个使用大量反射的插入式灵活框架,这决不是一个小问题。由于害怕出错而不敢应用重构(或者使其代价昂贵)必然会限制您改进设计和完善代码的能力。那么,试着替换魔力字符串(magic strings)和松散类型的Type数组将会如何呢?
    MethodInfo info =
    Reflector.Method<Mock, string, int>(
     (x, y, z) => x.PublicMethodParameters(y, z));
     
    通过Linq进行强类型反射
    其工作原理是作为参数传递的λ表达式就像前一版本.NET中的委托一样不一定要执行。
    上面的代码基本上构造了一个可以调用类型上给定方法的λ表达式。声明方法的目标类型的类型就是Method<> static generic方法的第一个类型参数。您可指定的可选类型参数将是您要调用的方法的参数类型(如果存在)。如果我想获得无参数方法的MethodInfo,则表达式将是:
    MethodInfo info = Reflector.Method<Mock>(
            x => x.PublicMethodNoParameters());
    这比您以前见到的任何λ表达式都典型。在λ表达式中,如果您需要传递附加参数,则必须将所有内容放到括号中(上例中的xyz)。用于属性和字段的类型映射功能是相同的:
    PropertyInfo property =
     Reflector.Property<Mock>(x => x.PublicProperty);
    FieldInfo field =
     Reflector.Field<Mock>(x => x.PublicField);
     
    利用表达式树
    现在我们开始讨论比较有趣的内容,即如何实现它。
    Linq任何接收λ表达式委托类型的方法都可以转换为接收相同委托类型的Expression<T>的方法并且不需要更改客户机代码。例如
    privatestaticvoid DoSomething(Predicate<Mock> predicate)
    可以替换为:
    privatestaticvoid DoSomething(
        Expression<Predicate<Mock><Mock>> predicate)
    在上述两种情况下调用代码可以是相同的λ表达式
    DoSomething(x => x.Value > 25);
    这里发生的情况是编译器不会将指针传入到第二个方法签名的匿名委托中而是生成以表达式树的形式构建AST抽象语法树IL代码。如果您打开Reflector(我的类型反射类的名字也由此而来,它是任何高级开发人员都应该经常使用的最伟大的工具)并取消对DoSomething的方法调用,就可以看到:
    ParameterExpression expression1 =
        Expression.Parameter(typeof(Mock), "x");
    Program.DoSomething(
     Expression.Lambda<Predicate<Mock>>(
        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>以及TArg0……),因为它们返回的是布尔值。我需要更灵活的对象,简单来说就是返回对象的对象,以及接收一些固定参数类型(例如Func<T>)的委托“重载”。因此我得到如下内容:
    publicdelegateobjectOperation();
    publicdelegateobjectOperation<T>(T declaringType);
    publicdelegateobjectOperation(T declaringType, A0 arg0);
    ...
    注意API的用户从来都不会知道这些委托类型的对象的存在就像查询操作符的用户从不知道Func<T>的存在一样。我希望将来这些委托能够消失,而代之以更好的东西(可能是publicdelegateobject Operation < params T> ;))。此外,注意我是如何将新的参数类型的参数添加到T后面的,T是重载的通用转换,与LinqFunc<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);
    }
    ...
    好了重点到底是什么由于能够指定参数类型并在λ表达式中声明它们您通常能够获得强类型和编译类型的安全性即使您在结尾处使用了反射。回到最开始的例子:
    MethodInfo info = Reflector.Method<Mock, string, int>(
           
    (x, y, z) => x.PublicMethodParameters(y, z));
    看一下如何使yz参数成为强类型参数,并用于在编译时找出λ表达式中调用的是哪个方法。完全更新IDE以便与C# 3.0一起使用时,给定参数的重命名重构工具可提供期望的结果:它在任何情况下都会重命名,就像您使用它时一样,即使对于反射也是如此!并且,如果您更改参数类型,代码将会编译失败!

    发表于 @ 2006年09月29日 11:46:00|评论(loading...)|编辑

    新一篇: World Cup——世界杯的另类读法 | 旧一篇: 西摩•克雷(Seymour Cray)――隐居丛林的超级计算机之父

    评论

    #不知 发表于2006-10-07 08:43:00  IP: 221.232.163.*
    粗粗看了一下,怎么有点像java得reflect+模板?
    #鐚尗 发表于2006-10-07 22:15:00  IP: 202.104.18.*
    反射+泛型
    #不知 发表于2006-10-08 14:05:00  IP: 219.140.167.*
    的确是反射+泛型,见笑了:)
    #yidianyuan007 发表于2007-07-20 17:53:39  IP: 58.218.11.*
    坚决同意楼上意见
    #skyover 发表于2007-11-28 10:57:05  IP: 121.204.210.*
    现在C#的代码风格也越来越怪异了,呵呵。
    #lifangx 发表于2007-12-04 13:26:25  IP: 61.191.180.*
    同意楼上意见
    #indexchen 发表于2007-12-31 19:18:38  IP: 58.33.236.*
    函数式编程风格,java不知何时能提供类似LINQ的功能
    #chunchun_chengcheng 发表于2008-08-01 17:46:31  IP: 59.49.13.*
    java 多一些怎么样?
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © 《程序员》编辑