跟着AutoMapper学反射,你学会了吗?

最近看AutoMapper源码,被1行代码震惊到了。

请各位工程师也look一下:

private static readonly MethodInfo ContextMapMethod =
    ExpressionFactory.Method<ResolutionContext, object>(a => a.Map<object, object>(null, null, null)).GetGenericMethodDefinition(); 

看到这个代码本人震惊到了。

第一眼的反应是出bug了,测试代码写错地方了,那些object和null是认真的吗?

但直觉这不可能是bug!!!

 

一、赶紧测试一下

1. 于是把部分代码复制过来做了一个简单的测试

public static MethodInfo Method<T>(Expression<Func<T>> expression) => GetExpressionBodyMethod(expression);

public static MethodInfo Method<TType, TResult>(Expression<Func<TType, TResult>> expression) => GetExpressionBodyMethod(expression);

private static MethodInfo GetExpressionBodyMethod(LambdaExpression expression) => ((MethodCallExpression)expression.Body).Method;
public static int Sqrt(int x)
    => x * x;

int x = 3;
var sqrtMethod = Method<int, int>(x => Sqrt(x));
var result = sqrtMethod.Invoke(null, [x]);
// result = 9

不出意料,非常成功

2. 以前怎么反射方法呢

var sqrtMethod0 = typeof(MyTests).GetMethod("Sqrt");

3. 那两种方法哪种更好呢

初看原来的方法更简单,其实不然。

其一、原方法写死方法名

如果方法重命名,甚至增减参数,只有到运行时才报错,简直是埋了颗地雷啊

新方法就不一样了,重命名用vs重构就能直接适应

参数增、减可能会直接导致编译出错,以便及时处理

其二、如果方法有重载,更显得新方法的优势

var sqrtMethod0 = typeof(MyTests).GetMethod("Sqrt", BindingFlags.Static | BindingFlags.Public, [typeof(int)]);

方法有重载就需要提供参数类型列表和修饰符,新方法都不用,表达式就像用指针调用方法一样

 

二、这种方法还可以变通使用

1. 再建一个新的辅助方法

public static MethodInfo GetActionMethodInfo<TArgument>(Expression<Action<TArgument>> expression)
    => GetExpressionBodyMethod(expression);

2. 重写前面的例子

var sqrtMethod2 = GetActionMethodInfo<int>(x => Sqrt(x));

用Action的表达式反射Func,是不是很神奇

再结合.net的协变和逆变,能用更简单的方式构建调用方法的表达式

只要能构建一个调用方法的表达式就能反射出该方法

 

三、用表达式还可以反射属性、字段、索引器和构造函数等

 

通过.net表达式来反射是不是更优雅,今天的内容你学会了吗? 

 

posted on 2025-08-19 11:31  xiangji  阅读(56)  评论(0)    收藏  举报

导航