在优快云 上 看了一篇Daniel Cazzulinode的文章:Linq的超越
(原文链接http://blog.youkuaiyun.com/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,

new
string
[]
{ "Name", "Age" }
,

new
string
[]
{ "姓名", "年龄" }
);)
假如我们以后认为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的结构,自然就明白了。