C#反射调用泛型方法返回Action委托转换为null问题求解
问题原因
- 你通过
MakeGenericMethod(mytype)构造的泛型方法,返回值类型是Action<动态生成类型, object>,并非Action<object, object>。C#中委托的变体转换不支持反射生成的自定义类型与object之间的装箱转换场景,直接用as强转失败就会返回null。 - 你尝试的多层反射调用写法报错,是因为调用委托的
Invoke方法时,第一个参数传入了类型名称字符串,而非委托实例本身,自然会触发“对象与目标类型不匹配”的错误。 - 这个需求完全可以实现,不需要绕多层反射,有两种稳定的实现方案。
方案1:调整FastInvoke为非泛型实现(推荐)
直接把原有泛型方法改成非泛型版本,表达式树内部自动完成实例类型的转换,直接返回Action<object, object>类型的委托,后续调用不需要做任何类型转换,性能也最优。
修改后的FastInvoke代码:
public static class FastInvoke { public static Func<object, object> BuildUntypedGetter(MemberInfo memberInfo) { var targetType = memberInfo.DeclaringType; var exInstance = Expression.Parameter(typeof(object), "t"); var exInstanceCvt = Expression.Convert(exInstance, targetType); var exMemberAccess = Expression.MakeMemberAccess(exInstanceCvt, memberInfo); var exConvertToObject = Expression.Convert(exMemberAccess, typeof(object)); var lambda = Expression.Lambda<Func<object, object>>(exConvertToObject, exInstance); return lambda.Compile(); } public static Action<object, object> BuildUntypedSetter(MemberInfo memberInfo) { var targetType = memberInfo.DeclaringType; var exInstance = Expression.Parameter(typeof(object), "t"); var exInstanceCvt = Expression.Convert(exInstance, targetType); var exMemberAccess = Expression.MakeMemberAccess(exInstanceCvt, memberInfo); var exValue = Expression.Parameter(typeof(object), "p"); var exConvertedValue = Expression.Convert(exValue, GetUnderlyingType(memberInfo)); var exBody = Expression.Assign(exMemberAccess, exConvertedValue); var lambda = Expression.Lambda<Action<object, object>>(exBody, exInstance, exValue); return lambda.Compile(); } private static Type GetUnderlyingType(this MemberInfo member) { switch (member.MemberType) { case MemberTypes.Event: return ((EventInfo)member).EventHandlerType; case MemberTypes.Field: return ((FieldInfo)member).FieldType; case MemberTypes.Method: return ((MethodInfo)member).ReturnType; case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; default: throw new ArgumentException("Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"); } } }
对应调用代码简化为:
Type mytype = MyTypeBuilder.CompileResultType(dtTempAttendance, true); var obj = Activator.CreateInstance(mytype); PropertyInfo prop = mytype.GetProperty("EmployeeName", BindingFlags.Public | BindingFlags.Instance); // 直接获取setter,类型就是Action<object, object>,无需转换 var setter = FastInvoke.BuildUntypedSetter(prop); setter(obj, "test value");
方案2:保留原有泛型实现,做委托包装
如果你不想修改原有FastInvoke代码,可以在反射拿到泛型委托后,通过Delegate.CreateDelegate包装成目标类型,不需要多层反射调用:
Type mytype = MyTypeBuilder.CompileResultType(dtTempAttendance,true); var obj = Activator.CreateInstance(mytype); PropertyInfo prop = mytype.GetProperty("EmployeeName", BindingFlags.Public | BindingFlags.Instance); Type openGeneric = typeof(FastInvoke); MethodInfo method = openGeneric.GetMethod("BuildUntypedSetter", BindingFlags.Static | BindingFlags.Public) .MakeGenericMethod(mytype); // 先拿到原始泛型委托 var genericSetter = (Delegate)method.Invoke(null, new[] { prop }); // 包装为Action<object, object> var setter = (Action<object, object>)Delegate.CreateDelegate(typeof(Action<object, object>), genericSetter, "Invoke"); setter(obj, "test value");
注意:你之前写的多层反射Invoke写法性能极差,每一次赋值都会走两次反射逻辑,完全失去了表达式树预编译的性能优势,不建议使用。如果一定要通过反射调用委托,正确写法如下,但依然不推荐:
var setterDelegate = method.Invoke(null, new[] { prop }); var invokeMethod = setterDelegate.GetType().GetMethod("Invoke"); invokeMethod.Invoke(setterDelegate, new object[] { obj, "1" });
内容的提问来源于stack exchange,提问作者user3103982
相关产品推荐
相关产品推荐

