如何创建可容纳多种泛型IThingy类型的C#枚举集合?
问题描述
我定义了一个带泛型的接口IThingy<T>,包含一组共享属性:
public interface IThingy<T> { public string Name { get; init; } public int Priority { get; init; } public T Value { get; set; } }
多个类实现了这个接口,例如:
public class TextThingy : IThingy<string?> { public string Name { get; init; } = "A Text Thingy"; public int Priority { get; init; } public string? Value { get; set; } } public class NumberThingy: IThingy<int?> { public string Name { get; init; } = "A Number Thingy"; public int Priority { get; init; } public int? Value { get; set; } }
现在需要一个ThingyGroup类来存放不同类型的IThingy<T>实例集合,用于遍历生成API的JSON:
public class ThingyGroup { public string Name { get; init; } = "A Group of Related Thingies"; public IEnumerable<IThingy<???>> Thingies { get; init; } = new List<IThingy<???>>(); }
但直接省略类型参数会报错,设为object或dynamic时,添加TextThingy或NumberThingy实例到列表也会报错,请问这里应该用什么类型才能让列表容纳不同类型的Thingy?
解决方案
方法1:定义非泛型基接口
这是最通用且无局限性的方案,通过创建非泛型基接口统一共享属性,让泛型接口继承它:
// 非泛型基接口,存放无需泛型约束的共享属性 public interface IThingy { string Name { get; init; } int Priority { get; init; } // 新增非泛型Value属性,用于统一访问值(兼容序列化) object? Value { get; } } // 泛型接口继承非泛型基接口 public interface IThingy<T> : IThingy { // 使用new关键字覆盖基接口的Value属性,保留泛型类型的读写能力 new T Value { get; set; } }
实现类需要显式实现非泛型接口的Value属性,确保类型转换正确:
public class TextThingy : IThingy<string?> { public string Name { get; init; } = "A Text Thingy"; public int Priority { get; init; } public string? Value { get; set; } // 显式实现非泛型接口的Value属性 object? IThingy.Value => Value; } public class NumberThingy: IThingy<int?> { public string Name { get; init; } = "A Number Thingy"; public int Priority { get; init; } public int? Value { get; set; } object? IThingy.Value => Value; }
最后ThingyGroup就可以使用非泛型的IThingy类型:
public class ThingyGroup { public string Name { get; init; } = "A Group of Related Thingies"; public IEnumerable<IThingy> Thingies { get; init; } = new List<IThingy>(); }
此时所有实现IThingy<T>的类实例都能添加到Thingies集合中,遍历和JSON序列化(如System.Text.Json、Newtonsoft.Json)都能正常工作,同时保留Value的读写能力。
方法2:利用泛型协变(只读场景专用)
如果业务场景中Value属性只需要读取、不需要修改,可以将泛型接口声明为协变(使用out关键字):
// 协变泛型接口,Value仅保留只读属性 public interface IThingy<out T> { string Name { get; init; } int Priority { get; init; } T Value { get; } // 移除set访问器 }
此时ThingyGroup可以将类型参数设为object:
public class ThingyGroup { public string Name { get; init; } = "A Group of Related Thingies"; public IEnumerable<IThingy<object>> Thingies { get; init; } = new List<IThingy<object>>(); }
协变特性允许将IThingy<string?>、IThingy<int?>等隐式转换为IThingy<object>,从而存入同一集合。但该方案的局限性很明显:必须牺牲Value的写能力,仅适用于只读场景。
内容的提问来源于stack exchange,提问作者Kevin Whiteside
相关产品推荐
相关产品推荐

