[翻译]使用表达式树获取对象、类型和成员的信息
让我们从一个简单任务开始。假设你需要输出字段或属性的名字和它的值。例如,设想你有下面的类:
{
public string SomeField = "Test";
}
Console.WriteLine("SomeField : {0}", sample.SomeField);
// 输出 SomeField : Test
上面代码的问题是,你使用了硬编码的字符串SomeField。没有任何保证这就是字段的真实名称。你可能敲错字母或是意外的指定了错误名称。另外,如果你重命名你的属性为AnotherField,必须手动找到所有表示它的名字的字符串。
{
var member = (MemberExpression)e.Body;
return member.Member.Name;
}
GetName(() => sample.SomeField), sample.SomeField);
// 同样输出 SomeField : Test
因为用lambda表达式,你不仅拥有编译时错误检查,而且在键入成员名称时有完全的智能感知支持。并且如果你重命名此属性,编译器将找出所有使用了此属性的地方。或者依靠重构工具来重命名属性所有的使用代码。
事实上,你同样可以使用这个方法来获得变量本身的名称(这对跟踪和记录日志都非常方便)。
//输出 sample : SampleClass
使用lambda表达式仅有的一个问题是你需要确保使用者输入正确。例如,我们的方法的使用者可以传递一个像这样的lambda表达式()=>new SampleClass()。很不幸,你无法为这得到编译检查。那么,当我们使用lambda表达式时,请确保你真的能得到你预期的表达式。例如像这样:
public static string GetName<T>(Expression<Func<T>> e)
{
var member = e.Body as MemberExpression;
// 如果当前方法得到一个不是成员访问的
// lambda表达式,
// 例如, () => x + y, 则抛出一个异常
if (member != null)
return member.Member.Name;
else
throw new ArgumentException(
"'" + e +
"': 对此方法来讲不是有效的表达式");
}
另一场景是表达式树可以帮助你获得类型的成员信息。C#提供typeof操作符来获得类型信息,但没有提供像memberof或infoof这样的操作符。我们可以使用反射方法比如Type.GetField(),Type.GetMethod(),等等,但这时你不得不又使用字符串。
Type type = typeof(SampleClass);
// 但当使用反射获取成员信息,你不得不依赖字符串
FieldInfo fieldInfo = type.GetField("SomeField");
Console.WriteLine(fieldInfo);
// 输出 System.String SomeField
如果在你的类里使用了重载,那么问题变得更糟糕。
{
public string SomeField = "Test";
public void SomeMethod() { }
public void SomeMethod(string arg) { }
}
"SomeMethod", new Type[] { typeof(String) });
Console.WriteLine(methodInfo);
// 输出 Void SomeMethod(System.String)
你可以通过表达式树获得相同的信息而不需要硬编码字符串,同时使编译器检查是否存在你需要的成员。此外,要获取所需的重载方法只要在lambda表达式中提供此方法的使用实例。谢谢Mads Torgersen,一个Visual Studio Program Manager,提供下面的代码样例。
public static MemberInfo MemberOf<T>(Expression<Func<T>> e)
{
return MemberOf(e.Body);
}
// 我们需要添加这个重载来覆盖方法没有返回值的情况
public static MemberInfo MemberOf(Expression<Action> e)
{
return MemberOf(e.Body);
}
private static MemberInfo MemberOf(Expression body)
{
{
var member = body as MemberExpression;
if (member != null) return member.Member;
}
{
var method = body as MethodCallExpression;
if (method != null) return method.Method;
}
throw new ArgumentException(
"'" + body + "': 不是一个成员访问");
}
现在你可以在所有场合使用MemberOf方法,实例成员和静态成员都行。
Console.WriteLine(MemberOf(() => sample.SomeField));
// 输出 System.String SomeField
// 用来选择一个特定方法重载,你在这里简单的展示方法的用法
Console.WriteLine(MemberOf(() => sample.SomeMethod("Test")));
// 输出 Void SomeMethod(System.String)
Console.WriteLine(MemberOf(() => sample.SomeMethod()));
// 输出 Void SomeMethod()
Console.WriteLine(MemberOf(() => Console.Out));
// 输出 System.IO.TextWriter Out
我想重申一下,这个功能已经在C#3.0/.NET framework 3.5有效。如果你想知道表达式树在.NET framework 4里做了怎样的扩展,看看我之前的文章: Generating Dynamic Methods with Expression Trees in Visual Studio 2010.
转载需注明出处:http://www.cnblogs.com/tianfan/