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

C#反射调用泛型方法时如何在运行时动态生成匹配类型的Func委托

问题说明

存在一个需要通过反射调用的外部泛型方法,签名如下:
Foo<TResult>(Func<Bar, TResult> factory)
本次实现的核心难点是调用逻辑中的TResult类型无法在编译期确定,只能在运行时动态获取,需要支持全动态的类型处理。

最初尝试传入如下定义的委托作为反射调用Foo方法的参数:

Func<Bar, dynamic> factory = (bar) =>
{
    return tResultInstance;
};

说明:上述实现确实不需要使用入参Bar,并非代码笔误。
实际反射调用Foo方法时会抛出类型转换异常,提示无法将System.Object类型转换为TResult类型。
核心诉求:确认是否可以在运行时动态创建类型完全匹配的Func委托,用于泛型方法的反射调用。


实际业务复现场景

以动态调用依赖注入框架的AddScoped方法为例:

静态调用写法

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<Baz>(_ => new Baz("string"));

初始动态实现(报错版本)

private const string AddScopedMethodName = "AddScoped";
private const string AddScopedMethodSignature = "Microsoft.Extensions.DependencyInjection.IServiceCollection AddScoped[TService](Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Func`2[System.IServiceProvider,TService])";

<...>

// bazType为运行时获取的Baz类型
var bazConstructorInfo = bazType.GetConstructor(new Type[1]
{
    typeof(String)
});
var bazConstructorParameters = new object[1]
{
    inputString // inputString值为"string"
};
var bazInstance = bazConstructorInfo.Invoke(bazConstructorParameters);

var addScopedMethodInfo = typeof(ServiceCollectionServiceExtensions)
    .GetMethods()
    .Where(method =>
        method.IsPublic &&
        method.IsGenericMethod &&
        (method.Name == AddScopedMethodName) &&
        (method.ToString() == AddScopedMethodSignature))
    .Single();
var addScopedMethod = addScopedMethodInfo.MakeGenericMethod(bazType);
Func<IServiceProvider, dynamic> implementationFactory = (serviceProvider) =>
{
    return bazInstance;
};
var addScopedMethodParameters = new object[2]
{
    services, // services为IServiceCollection实例
    implementationFactory
};
addScopedMethod.Invoke(
    services,
    addScopedMethodParameters); 
    // 抛出异常:Object of type 'System.Func`2[System.IServiceProvider,System.Object]' cannot be converted to type 'System.Func`2[System.IServiceProvider,Baz]'

运行上述代码会抛出类型转换异常,提示无法将System.Func2[System.IServiceProvider,System.Object]类型的对象转换为System.Func2[System.IServiceProvider,Baz]类型。


已排除的无效方案

有参考方案提出可以通过反射获取预先定义的、返回固定类型的静态方法来构造对应委托,示例代码如下:

static Foo CreateFoo() { return new Foo(); }

<...>

var method = typeof(Program)
    .GetMethod(
        "CreateFoo",
        BindingFlags.NonPublic | BindingFlags.Static);

该方案不适用于当前场景:本场景中具体服务类型在运行前完全未知,无法预先编写返回固定类型的静态方法,无法解决问题。


可行解决方案

可以通过Delegate.CreateDelegate方法在运行时动态构造类型完全匹配的Func委托,不需要预先定义固定类型的静态方法,完全适配全动态类型场景。
针对上述AddScoped的示例,修正后的核心代码如下:

// 前置逻辑:获取泛型方法、构造服务实例的代码保持不变
// 1. 构造目标委托的精确类型:Func<入参类型, 运行时获取的TResult类型>
var targetFuncType = typeof(Func<,>).MakeGenericType(typeof(IServiceProvider), bazType);
// 2. 写通用逻辑的委托模板,入参、返回值都用object即可
Func<IServiceProvider, object> factoryTemplate = sp => bazInstance;
// 3. 基于模板委托创建类型完全匹配的目标委托实例
var matchedFactory = Delegate.CreateDelegate(targetFuncType, factoryTemplate.Target, factoryTemplate.Method);
// 4. 传入类型匹配的委托,反射调用泛型方法即可正常执行
var addScopedMethodParameters = new object[2]
{
    services,
    matchedFactory
};
addScopedMethod.Invoke(services, addScopedMethodParameters);

如果需要在委托里加额外处理逻辑,直接在factoryTemplate里写即可,不需要提前知道具体的TResult类型。

报错原因说明:dynamic本质是C#提供的语法糖,编译后Func<Bar, dynamic>会被统一编译为Func<Bar, object>,和泛型方法要求的Func<Bar, 具体TResult类型>是完全独立的委托类型,委托不支持这种跨泛型参数的隐式类型转换,所以直接传必然抛出类型转换异常,必须在运行时构造和目标泛型参数完全匹配的委托实例。


内容的提问来源于stack exchange,提问作者Ivan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 01:30:57