C#如何在运行时动态实例化类型并调用泛型方法
核心原理
C#的泛型是静态泛型,泛型类型参数必须在编译阶段可确定,原代码中直接用Type类型的变量type作为List<>和StoreData<>的泛型参数是无法通过编译的。运行时动态指定泛型参数的标准实现方式是通过反射,基于开放泛型定义构造闭合泛型类型/方法后调用。
具体实现
首先修正原代码里的两处基础语法错误:
InventoryType枚举中House项后缺少逗号- 静态类
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
相关产品推荐
相关产品推荐

