利用Lambda表达式: 重构代码时自动更新反射成员。

来源:互联网 发布:软件快速开发平台 编辑:程序博客网 时间:2024/06/02 05:26
在CSDN 上 看了一篇Daniel Cazzulinode的文章:Linq的超越
(原文链接http://blog.csdn.net/programmer_editor/archive/2006/09/29/1305859.aspx)
    挺有意思的,自己也研究了一下.

    题目可能没有说清楚,那么咱么现在考虑一个场景:
    为了能够显示不同数据对象的数据,提高可复用性,你写了一个继承自DataGridView的控件,里面有一个Load()的公共方法,用来载入数据。
       public void LoadData(object[] items,string[] propertyNames,string[] headerText)
    items         是某一个类型的对象数组。
    propertyNames 是一组要显示数据的属性名
    headerText    是对应的Column的headerText
    在Load()方法中,遍历items的每一个元素。并且用反射的方法,获取指定属性名的数据,显示在DataGridView里。
    例如我们要显示一组人的姓名和年龄:
Load(Persons,
newstring[] {"Name","Age" },
newstring[] {"姓名","年龄" });)

    假如我们以后认为Name这个属性名并不好,并且想用NickName替代,利用VS强大的功能,很容易就可以完成重构,但是问题来了,我们还要手动去更改Load()调用的参数,因为我们传入的是固定的字符串,VS并不会自动的帮助我们进行更改。
    在Lambda引入之前,我们是没有办法完成这个功能的,但是现在我们利用Lambda Expression便可以实现。

    首先,设计一个类
       public class TestClass
       
{
           
public string Name;
           
public string PName {set; get; }
           
public string GetName()
           
{
               
return Name;
            }

           
public int Age;
           
public int PAge {set; get; }
           
public int GetAge()
           
{
               
return Age;
            }

           
public TestClass GetInstance()
           
{
               
return new TestClass();
            }

        }

下面这个方法是关键,我们传入一个Lambda表达式,她将返回一个MemberInfo,只是这个表达式是有要求的,她的形式是 "参数 => 成员调用"。
由于返回的是MemberInfo,是
PropertyInfo,MethodInfo等的基类。而事实上,如果我们的"成员调用"是一个Property那么此方法实际上返回的就是一个PropertyInfo,我们只要将它转化一下就可以了。
       public static MemberInfo GetMemberInfo<T>(Expression<Func<T,object>> exp)
       
{
            Expression texp
= exp.Body;
           
if (texp.NodeType== ExpressionType.Convert)
                texp
= ((UnaryExpression)texp).Operand;

           
if (texp.NodeType== ExpressionType.Call)
               
return ((MethodCallExpression)texp).Method;
           
if (texp.NodeType== ExpressionType.MemberAccess)
               
return ((MemberExpression)texp).Member;
           
return null;
        }
    来做一个测试
       static void Main(string[] args)
       
{
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.Age).Name);
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.PAge).Name);
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.GetAge()).Name);
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.Name).Name);
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.PName).Name);
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.GetName()).Name);
            Console.WriteLine(GetMemberInfo
<TestClass>(p=> p.GetInstance()).Name);
        }

    下面是测试结果:


    细心的你也许已经发现,其实我们传入的Lambda表达式并没有真正的被编译并且执行。所以假如我们要传入 "p=>p.someMethod(.....)" 而someMethod这个方法是有参数的,那么我们只需简单的传入一个 null 就可以了,就像这样"p=>p.someMethod(null,null...)" 。

    至于GetMemberInfo<T>(..)方法为什么要那么实现,就留给读者自己动手试一下吧,只要在Debug状态下查看一下Expression的结构,自然就明白了。