65.9K
CodeProject 正在变化。 阅读更多。
Home

Strong:无需魔术字符串的反射

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (15投票s)

2008 年 8 月 11 日

CPOL

3分钟阅读

viewsIcon

51145

一个使用 C# 3.0 编译器安全地检索 MemberInfos 的类。

引言

这是一篇解决特定问题的短文。如果您不介意在代码中使用魔术字符串,那么这里没有什么可看的...

.NET Framework 中的许多方法都使用字符串来标识代码标记。反射是一个明显的领域,但还有其他一些领域,例如 ObjectDataSource。这些字符串的问题在于它们对编译器和 IDE 是不透明的。这实际上意味着您无法重构代码、使用简单的混淆,甚至无法执行“查找所有引用”并依赖结果。我认为这是一个主要问题,所以我找到了另一种方法,在这里呈现。

背景

此解决方案依赖于 C# 3.0 编译器中的一项新功能。当 lambda 表达式被分配给类型为 System.Linq.Expressions.Expression<TDelegate> 的变量、字段或参数时,编译器会发出指令以构建表达式树,而不是将 lambda 编译为 IL。表达式树是代码语句的数据表示形式,类似于反射提供类型的数据表示形式。正如您从命名空间中可以猜到的那样,这被引入以支持 LINQ,但它也启用了此解决方案。

Lambdas 是编译器可以检查的真实标记,表达式树是在运行时可以检查的真实数据结构。因此,如果您编写一个指定特定成员(字段、属性或方法)的 lambda,您可以在代码中检查表达式树以查找该成员。

因此,在 C# 3.0 中,编译器仅向前推进到足以启用此解决方案的地步。它解析源文件,但它不是一直进行到 IL,而是以运行时易于访问的形式生成输出。

这里是一些链接

Manuel Abadia 编写了一个非常好的 表达式树图形调试器可视化工具 [^]。

VS2008 C# 示例中也有一个基本的文本可视化工具 (MSDN [^])。

Strong 类

Strong 类非常小,所以我已在此处完整包含。LambdaExpressionExpression<TDelegate> 的基类。有趣的成员是最后四个方法。

public delegate void Action<A, B, C, D, E>
( A a, B b, C c, D d, E e );

public delegate void Action<A, B, C, D, E, F>
( A a, B b, C c, D d, E e, F f );

public delegate void Action<A, B, C, D, E, F, G>
( A a, B b, C c, D d, E e, F f, G g );

public delegate void Action<A, B, C, D, E, F, G, H>
( A a, B b, C c, D d, E e, F f, G g, H h );

public delegate void Action<A, B, C, D, E, F, G, H, I>
( A a, B b, C c, D d, E e, F f, G g, H h, I i );

public static class Strong
{
  public static class Static
  {
    public static FieldInfo Field<T>
    ( Expression<Func<T>> m )
    { return GetFieldInfo( m ); }
    
    public static PropertyInfo Property<T>
    ( Expression<Func<T>> m )
    { return GetPropertyInfo( m ); }
    
    public static MethodInfo Method
    ( Expression<Action> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1>
    ( Expression<Action<T1>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2>
    ( Expression<Action<T1, T2>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3>
    ( Expression<Action<T1, T2, T3>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4>
    ( Expression<Action<T1, T2, T3, T4>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5>
    ( Expression<Action<T1, T2, T3, T4, T5>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6>
    ( Expression<Action<T1, T2, T3, T4, T5, T6>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7>
    ( Expression<Action<T1, T2, T3, T4, T5, T6, T7>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7, T8>
    ( Expression<Action<T1, T2, T3, T4, T5, T6, T7, T8>> m )
    { return GetMethodInfo( m ); }
  }
  
  public static class Instance<TClass>
  {
    public static FieldInfo Field<T>
    ( Expression<Func<TClass, T>> m )
    { return GetFieldInfo( m ); }
    
    public static PropertyInfo Property<T>
    ( Expression<Func<TClass, T>> m )
    { return GetPropertyInfo( m ); }
    
    public static MethodInfo Method
    ( Expression<Action<TClass>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1>
    ( Expression<Action<TClass, T1>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2>
    ( Expression<Action<TClass, T1, T2>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3>
    ( Expression<Action<TClass, T1, T2, T3>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4>
    ( Expression<Action<TClass, T1, T2, T3, T4>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5, T6>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5, T6, T7>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7, T8>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5, T6, T7, T8>> m )
    { return GetMethodInfo( m ); }
  }
  
  static FieldInfo GetFieldInfo( LambdaExpression lambda )
  { return ( FieldInfo ) GetMemberInfo( lambda ); }
  
  static PropertyInfo GetPropertyInfo( LambdaExpression lambda )
  { return ( PropertyInfo ) GetMemberInfo( lambda ); }
  
  static MemberInfo GetMemberInfo( LambdaExpression lambda )
  { return ( ( MemberExpression ) lambda.Body ).Member; }
  
  static MethodInfo GetMethodInfo( LambdaExpression lambda )
  { return ( ( MethodCallExpression ) lambda.Body ).Method; }
}

使用代码

静态 Strong 类没有公共方法。相反,它包含两个嵌套的静态类,StaticInstance,它们允许您指定所需的成员类型。Instance 类接受一个泛型类型参数,该参数应该是包含类。

这两个类都提供了名为 FieldPropertyMethod 的公共方法,这些方法被重载。它们分别返回 FieldInfoPropertyInfoMethodInfo。这些类都派生自 MemberInfoMemberInfo 具有 Name 属性,等等。

最简单的方法是展示一些例子。它们都作用于此类

class Class
{
  public static int StaticField = 42;
  public static int StaticProperty { get; set; }
  public static void StaticAction() { }
  public static void StaticAction( int i ) { }
  public static int StaticFunc() { return 42; }
  public static int StaticFunc( int i ) { return i; }
  
  public int Field = 42;
  public int Property { get; set; }
  public void Action() { }
  public void Action( int i ) { }
  public int Func() { return 42; }
  public int Func( int i ) { return i; }
}

最后,这里是示例

Strong.Static.Field( () => Class.StaticField );
Strong.Static.Property( () => Class.StaticProperty );
Strong.Static.Method( () => Class.StaticAction() );
Strong.Static.Method<int>( i => Class.StaticAction( i ) );
Strong.Static.Method( () => Class.StaticFunc() );
Strong.Static.Method<int>( i => Class.StaticFunc( i ) );

Strong.Instance<Class>.Field( o => o.Field );
Strong.Instance<Class>.Property( o => o.Property );
Strong.Instance<Class>.Method( o => o.Action() );
Strong.Instance<Class>.Method<int>( ( o, i ) => o.Action( i ) );
Strong.Instance<Class>.Method( o => o.Func() );
Strong.Instance<Class>.Method<int>( ( o, i ) => o.Func( i ) );

结论

这是我想到的解决魔术字符串问题的最简洁的方案。但是,它依赖于 C# 3.0 编译器,这对于某些人来说可能是一个问题。您可以使用 C# 2.0 中的委托做类似的事情,但是您必须从编译后的 IL 代码中提取内部引用,这有点像一个黑客。C# 3.0 中新的表达式树使此解决方案更加容易。

感谢您阅读本文;希望您喜欢它。

© . All rights reserved.