使用表达式树访问对象、类型及成员(上):获取类型和成员的信息
当我们要打印一个字段或属性的值时,通常会采用如下的方法:
public class SomeClass { public string SomeField = "Some Field value"; } SomeClass someClass = new SomeClass(); Console.WriteLine("Some Field :" + someClass.SomeField);
对于这种硬编码的坏处自然不言而喻,拼写错误等等是小事,一旦名称更改,四处查找替换无异于一场灾难。也许我们可以通过一个静态字段来代替硬编码,但如果要打印的字段过多,逐一添加静态字段也是相当痛苦的事情。
对此我们可以使用表达式树来解决:
public static string GetName<T>(Expression<Func<T>> expr) { var member = expr.Body as MemberExpression; if (member != null) return member.Member.Name; throw new ArgumentException( "'" + expr + "': is not a valid expression for this method"); }
SomeClass someClass = new SomeClass(); Console.WriteLine("{0} : {1}", GetName(() => someClass.SomeField), someClass.SomeField);
表达式树给我们带来了智能感知和编译时语法错误提示,即使更改了字段或属性名称,也可以使用重构工具快速修复代码。
但是,在获取类型成员的数据时,我们还是会不可避免地使用硬编码字符串。我们可以使用typeof关键字来获取一个对象的类型而不必使用硬编码字符串,但是C#却没有提供相应的memberof。我们只能使用GetField或GetMethod等反射方法,将代表字段或方法名称的字符串作为参数传递进去。
Type type = typeof(SomeClass); MethodInfo method = type.GetMethod("SomeMethod");
如果SomeMethod方法包含重载方法,我们还不得不指定其参数。
Type type = typeof(SomeClass); MethodInfo method = type.GetMethod("SomeMethod", new Type[] { typeof(String) });
这样的API使用起来实在是太不方便了。不但要使用无法在编译时检查的硬编码字符串,参数的个数、类型等等还会因为方法签名的修改而大受牵连。
好吧,让我们用表达式树来改变这一切。
public static MemberInfo MemberOf<T>(Expression<Func<T>> e) { return MemberOf(e.Body); } // We need to add this overload to cover scenarios // when a method has a void return type. 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 + "': not a member access"); }
这样,我们就可以像调用方法一样来获取MemberInfo了。
Console.WriteLine(MemberOf(() => someClass.SomeField)); // Prints System.String SomeField // To choose a particular method overload, // you simply show the usage of the method. Console.WriteLine(MemberOf(() => someClass.SomeMethod("Test"))); // Prints Void SomeMethod(System.String) Console.WriteLine(MemberOf(() => someClass.SomeMethod())); // Prints Void SomeMethod() Console.WriteLine(MemberOf(() => Console.Out)); // Prints System.IO.TextWriter Out
参考:
Getting Information About Objects, Types, and Members with Expression Trees