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

C#如何在运行时动态实例化类型并调用泛型方法

核心原理

C#的泛型是静态泛型,泛型类型参数必须在编译阶段可确定,原代码中直接用Type类型的变量type作为List<>和StoreData<>的泛型参数是无法通过编译的。运行时动态指定泛型参数的标准实现方式是通过反射,基于开放泛型定义构造闭合泛型类型/方法后调用。

具体实现

首先修正原代码里的两处基础语法错误:

  1. InventoryType枚举中House项后缺少逗号
  2. 静态类Main不能声明实例字段、实例构造函数

完整实现代码如下:

using System.Reflection;
using Newtonsoft.Json;

public enum InventoryType
{
    Car,
    Dog,
    House,
    Cat
}

public class Main
{
    private readonly IApiClient _apiClient;
    private readonly IPostgreSQL _postgreSql;
    // 提前缓存开放泛型方法定义,减少重复反射开销
    private readonly MethodInfo _storeDataGenericDefinition;

    public Main(IApiClient apiClient, IPostgreSQL postgreSql)
    {
        _apiClient = apiClient;
        _postgreSql = postgreSql;

        // 从IPostgreSQL类型中获取StoreData的开放泛型方法定义
        // 如果方法有重载,需要传入参数类型数组精确匹配目标方法
        _storeDataGenericDefinition = typeof(IPostgreSQL)
            .GetMethod("StoreData", BindingFlags.Instance | BindingFlags.Public)
            ?.GetGenericMethodDefinition();
        
        if (_storeDataGenericDefinition == null)
            throw new MissingMethodException("IPostgreSQL中未找到符合签名的StoreData泛型方法");

        // 遍历所有枚举值
        foreach (InventoryType inventoryType in Enum.GetValues<InventoryType>())
        {
            // 映射枚举到对应实体类型
            Type entityType = inventoryType switch
            {
                InventoryType.Car => typeof(Car),
                InventoryType.Dog => typeof(Dog),
                InventoryType.House => typeof(House),
                InventoryType.Cat => typeof(Cat),
                _ => throw new NotSupportedException($"不支持的库存类型:{inventoryType}")
            };

            // 拉取原始JSON数据
            string rawJson = _apiClient.GetRawData(inventoryType);
            // 构造对应List<实体类型>的Type对象
            Type listType = typeof(List<>).MakeGenericType(entityType);
            // 用非泛型反序列化接口传入目标类型,不需要反射构造泛型反序列化方法
            object data = JsonConvert.DeserializeObject(rawJson, listType);
            
            // 基于开放泛型方法,构造当前实体类型对应的闭合泛型方法 即StoreData<Car>/StoreData<Dog>等
            MethodInfo closedStoreMethod = _storeDataGenericDefinition.MakeGenericMethod(entityType);
            // 反射执行方法
            closedStoreMethod.Invoke(_postgreSql, new object[] { data });
        }
    }
}
性能优化方案

如果这段逻辑是高频调用场景,可以做两层缓存减少反射开销:

  • 把枚举和实体类型的映射提前存到字典里,避免每次循环执行switch判断
  • 提前把每个枚举值对应的闭合泛型方法缓存到字典中,不需要每次循环调用MakeGenericMethod

缓存示例:

private static readonly IReadOnlyDictionary<InventoryType, (Type EntityType, MethodInfo StoreMethod)> InventoryMetaCache = BuildInventoryCache();

private static IReadOnlyDictionary<InventoryType, (Type, MethodInfo)> BuildInventoryCache()
{
    // 这里提前初始化StoreData的开放泛型定义,和所有枚举对应的闭合方法
    MethodInfo openStoreMethod = typeof(IPostgreSQL)
        .GetMethod("StoreData", BindingFlags.Instance | BindingFlags.Public)
        .GetGenericMethodDefinition();

    Dictionary<InventoryType, (Type, MethodInfo)> cache = new();
    foreach (InventoryType type in Enum.GetValues<InventoryType>())
    {
        Type entityType = type switch
        {
            InventoryType.Car => typeof(Car),
            InventoryType.Dog => typeof(Dog),
            InventoryType.House => typeof(House),
            InventoryType.Cat => typeof(Cat),
            _ => throw new NotSupportedException()
        };
        MethodInfo closedMethod = openStoreMethod.MakeGenericMethod(entityType);
        cache.Add(type, (entityType, closedMethod));
    }
    return cache;
}

后续循环时直接从缓存取对应类型和方法即可,几乎没有额外反射开销。

注意事项
  • 如果StoreData是非公开方法,获取MethodInfo时需要加上BindingFlags.NonPublic标记
  • 如果StoreData是异步方法,反射调用后会返回Task对象,需要做await处理,不能直接丢弃返回值否则会丢失异常
  • 反序列化时也可以用反射构造JsonConvert.DeserializeObject<List<T>>的闭合泛型方法调用,但直接用非泛型重载传listType更简洁,性能没有差异

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:42:15