使用表达式树访问对象、类型及成员(上):获取类型和成员的信息

当我们要打印一个字段或属性的值时,通常会采用如下的方法:

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

posted @ 2010-01-07 16:33  麒麟.NET  阅读(1680)  评论(0编辑  收藏  举报