C#如何将运行时获取的Type对象赋值给泛型参数<T>
问题原因
- 泛型参数要求编译期确定类型,你直接把运行时获取的
Type对象schemaType作为泛型参数传入GetSchemaObjects<>是非法语法,C#编译器无法在编译阶段验证这个类型的合法性,所以会报错。 - 基类泛型定义存在逻辑错误:类已经声明了泛型参数
T,方法GetSchemaObjects<T>又重复声明了同名泛型参数,两个T是完全独立的,会导致实际运行时的类型匹配异常。 - 方法返回值要求是
List<T>,而你运行时动态生成的集合类型是List<schemaType>,编译期无法确认二者的兼容关系,所以会报类型转换错误。 - 如果
iMyInterface接口未声明GetSchemaObjects相关方法,你将实例强转为iMyInterface后也无法直接调用该方法,属于接口契约不匹配。
解决步骤
第一步:修正基类与接口定义
首先修正基类重复声明泛型的问题,同时补全接口契约:
public interface iMyInterface { // 声明非泛型版本方法,方便运行时统一调用 List<iMyInterface> GetSchemaObjects(string csvData); } // 移除方法上重复的泛型声明,直接使用类的泛型参数 public abstract class BaseSchema<T> : iMyInterface where T : iMyInterface { public List<T> GetSchemaObjects(string csvData) { var records = new List<T>(); using (var streamReader = new StreamReader(csvData)) using (var csvReader = new CsvReader(streamReader, CultureInfo.InvariantCulture)) { records = csvReader.GetRecords<T>().ToList(); } return records; } // 显式实现接口的非泛型方法,做类型转换适配 List<iMyInterface> iMyInterface.GetSchemaObjects(string csvData) { return GetSchemaObjects(csvData).Cast<iMyInterface>().ToList(); } }
第二步:修改动态调用逻辑
如果需要保留返回强类型List<T>的需求,可以通过反射构造泛型方法调用,修正后的代码如下:
public List<T> GetTestSchema<T>(string versionNumber, string schemaName) where T : iMyInterface { Type[] allSchemaTypes = Assembly.GetExecutingAssembly().GetTypes() .Where(t => t.IsClass && t.Namespace.StartsWith($"{schemaNamespace}") && !t.GetCustomAttributes(typeof(ObsoleteAttribute), true).Any()) .ToArray(); int indexNumber = GetIndexNumberOfRequiredVersion(versionNumber); Type schemaType = allSchemaTypes .Skip(indexNumber) .Where(x => x.Name.Equals(schemaName) && interfaceType.IsAssignableFrom(x)) .FirstOrDefault(); // 提前校验类型兼容性,避免后续转换报错 if (!typeof(T).IsAssignableFrom(schemaType)) { throw new InvalidOperationException($"查询到的Schema类型{schemaType.Name}与要求的返回类型{typeof(T).Name}不兼容"); } // 创建实例,不需要强转为iMyInterface var instance = Activator.CreateInstance(schemaType); // 反射获取方法并构造闭合泛型方法 var method = typeof(BaseSchema<>) .MakeGenericType(schemaType) .GetMethod("GetSchemaObjects", BindingFlags.Public | BindingFlags.Instance); // 调用方法并转换为要求的返回类型 var result = (List<T>)method.Invoke(instance, new object[] { @"CSV Path" }); return result; }
注意事项
- 如果你不需要返回强类型
List<T>,可以直接将方法返回值改为List<iMyInterface>,省去泛型匹配校验和反射构造的步骤,直接调用接口的非泛型方法即可。 - 确保运行时查询到的
schemaType确实继承自BaseSchema<>,否则反射获取方法会抛出异常。
内容的提问来源于stack exchange,提问作者Kiran More
相关产品推荐
相关产品推荐

