You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:51:20