如何正确访问与传递List<T>属性?泛型Excel导出遇类型转换报错
解决方案
问题出在泛型类的协变支持缺失:ExcelExport<TypeList1>和ExcelExport<object>是完全独立的类型,即使TypeList1继承自object,默认泛型类也不允许隐式转换。下面提供两种优雅的解决方式:
方式一:使用协变泛型接口(推荐)
通过定义带out关键字的泛型接口,让接口支持协变,这样不同T的ExcelExport<T>实例可以统一转换为IExcelExport<object>。
1. 定义协变接口
public interface IExcelExport<out T> { // 用IEnumerable<T>而非List<T>,因为IEnumerable<T>本身支持协变 IEnumerable<T> ExportList { get; } string SheetName { get; } }
2. 修改ExcelExport实现接口
public class ExcelExport<T> : IExcelExport<T> { public List<T> ExportList { get; set; } // 显式实现接口属性,返回IEnumerable<T> IEnumerable<T> IExcelExport<T>.ExportList => ExportList; public string SheetName { get; set; } }
3. 调整导出方法签名
// 直接接受IExcelExport<object>的集合,无需泛型参数(或保留泛型参数用T=object) public async Task<byte[]> ExportToExcelAsync(List<IExcelExport<object>> excelExports)
4. 修改调用代码
var export = new List<IExcelExport<object>>(); var list1 = getTypeList1Data(); export.Add(new ExcelExport<TypeList1>() { ExportList = list1, SheetName = "Sheet1" }); var list2 = getTypeList2Data(); export.Add(new ExcelExport<TypeList2>() { ExportList = list2, SheetName = "Sheet2" }); var result = await _excelFunctions.ExportToExcelAsync(export);
方式二:使用非泛型基类
如果不想引入接口,可以创建一个非泛型基类,让所有ExcelExport<T>继承它,统一导出方法的参数类型。
1. 定义非泛型基类
public abstract class ExcelExportBase { public abstract IEnumerable<object> ExportList { get; } public string SheetName { get; set; } }
2. 修改ExcelExport继承基类
public class ExcelExport<T> : ExcelExportBase { public new List<T> ExportList { get; set; } public override IEnumerable<object> ExportList => ExportList.Cast<object>(); }
3. 调整导出方法签名
public async Task<byte[]> ExportToExcelAsync(List<ExcelExportBase> excelExports)
4. 修改调用代码
var export = new List<ExcelExportBase>(); var list1 = getTypeList1Data(); export.Add(new ExcelExport<TypeList1>() { ExportList = list1, SheetName = "Sheet1" }); var list2 = getTypeList2Data(); export.Add(new ExcelExport<TypeList2>() { ExportList = list2, SheetName = "Sheet2" }); var result = await _excelFunctions.ExportToExcelAsync(export);
核心原因解释
泛型类默认不支持协变(即不能将SomeClass<Derived>隐式转换为SomeClass<Base>),但泛型接口可以通过out关键字声明协变,只要接口的泛型参数仅用作输出(返回值、只读属性)。方式一利用这一特性实现了类型安全的统一集合;方式二则通过基类的抽象属性统一访问入口,代价是需要将T转换为object。
内容的提问来源于stack exchange,提问作者chrisg229
相关产品推荐
相关产品推荐

