无法将泛型具体类型添加到泛型接口列表的技术问题
问题解决方案
问题根源
C# 中的泛型类默认不支持协变——哪怕 Student 实现了 IPerson,Person<Student> 和 Person<IPerson> 也会被视为完全无关的类型,无法直接转换。而直接存储 IPerson 列表能正常工作,是因为接口本身支持向上转型。
解决方案一:使用协变泛型接口
通过定义带 out 关键字的泛型接口启用协变,让 Person<TPerson> 实现这个接口,就能将 Person<Student> 隐式转换为 IPersonWrapper<IPerson> 类型存入列表。
修改后的代码
public interface IPerson { } // 定义协变泛型接口,out标记TPerson为协变类型参数 public interface IPersonWrapper<out TPerson> where TPerson : IPerson { DataSource Source { get; set; } TPerson Record { get; } // 协变类型仅能作为返回值,因此这里用只读属性 } public class Person<TPerson> : IPersonWrapper<TPerson> where TPerson : IPerson { public DataSource Source { get; set; } public TPerson Record { get; set; } } public class Student : IPerson { } public class StaffMember : IPerson { } public enum DataSource { /* 自定义枚举值 */ }
使用方式
var people = new List<IPersonWrapper<IPerson>>(); people.Add(new Person<Student>()); // 编译通过 people.Add(new Person<StaffMember>()); // 编译通过
注意:协变类型参数 TPerson 只能作为接口的返回值类型(比如属性的getter),不能作为方法参数或可写属性的setter,否则编译器会报错。
解决方案二:引入非泛型基类/接口
如果不需要在列表中直接访问泛型的 Record 属性,可以定义非泛型的基类或接口来统一存储所有 Person<TPerson> 实例,后续筛选时再做类型转换。
修改后的代码
public interface IPerson { } // 非泛型接口,包含公共属性 public interface IPersonWrapper { DataSource Source { get; set; } IPerson Record { get; } // 用IPerson类型暴露Record } public class Person<TPerson> : IPersonWrapper where TPerson : IPerson { public DataSource Source { get; set; } public TPerson Record { get; set; } // 实现非泛型接口的属性 IPerson IPersonWrapper.Record => Record; } public class Student : IPerson { } public class StaffMember : IPerson { } public enum DataSource { /* 自定义枚举值 */ }
使用方式
var people = new List<IPersonWrapper>(); people.Add(new Person<Student>()); people.Add(new Person<StaffMember>()); // 筛选特定类型的Person实例 var studentPeople = people.OfType<Person<Student>>(); // 筛选特定数据源的Person实例 var fromDbPeople = people.Where(p => p.Source == DataSource.Database);
这种方式不受协变规则限制,适合仅需统一存储、基于公共属性筛选的场景。
内容的提问来源于stack exchange,提问作者Tomuke
相关产品推荐
相关产品推荐

