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






4.86/5 (15投票s)
一个使用 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,而是以运行时易于访问的形式生成输出。
这里是一些链接
- MSDN:C# 3.0 语言规范 [^]
- CP 文章:探索 C# 中的 Lambda 表达式 [^]
- IanG 博客:表达式树 [^]
Manuel Abadia 编写了一个非常好的 表达式树图形调试器可视化工具 [^]。
VS2008 C# 示例中也有一个基本的文本可视化工具 (MSDN [^])。
Strong 类
Strong
类非常小,所以我已在此处完整包含。LambdaExpression
是 Expression<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
类没有公共方法。相反,它包含两个嵌套的静态类,Static
和 Instance
,它们允许您指定所需的成员类型。Instance
类接受一个泛型类型参数,该参数应该是包含类。
这两个类都提供了名为 Field
、Property
和 Method
的公共方法,这些方法被重载。它们分别返回 FieldInfo
、PropertyInfo
和 MethodInfo
。这些类都派生自 MemberInfo
,MemberInfo
具有 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 中新的表达式树使此解决方案更加容易。
感谢您阅读本文;希望您喜欢它。