泛型层级接口实现问题:如何修正代码编译错误?
代码编译错误的修复方案
问题场景
以下代码无法正常编译,需要修改TestRowViewModel或BucketT使其运行:
public interface IBase { public string BaseContent { get; } } public interface IHierarchy<T> : IBase where T : IBase { public List<T> Children { get; } } public class TestRowViewModel1 : IHierarchy<TestRowViewModel1>, IBase { public List<TestRowViewModel1> Children => [...]; public string BaseContent => [...]; } public class TestRowViewModel2 : IHierarchy<IBase>, IBase { public List<IBase> Children => [...]; public string BaseContent => [...]; } public class BucketConfig { public List<BucketT<IHierarchy<IBase>>> BucketList { get; } = new List<BucketT<IHierarchy<IBase>>>(); public BucketConfig() { var test1 = new BucketT<TestRowViewModel1>(); // [1] var test2 = new BucketT<TestRowViewModel2>(); // 编译通过 this.BucketList.Add(test1); // [2] this.BucketList.Add(test2); // [3] } } public class BucketT<T> where T : IHierarchy<IBase>, IBase { }
编译错误信息
- [1] CS0311:
TestRowViewModel1无法用作泛型类型或方法BucketT<T>的类型参数T。不存在从TestRowViewModel1到IHierarchy<IBase>的隐式引用转换。 - [2] CS1503: 无法将
TestRowViewModel1转换为BucketT<IHierarchy<IBase>>。 - [3] CS1503: 无法将
TestRowViewModel2转换为BucketT<IHierarchy<IBase>>。
修复方案
核心问题
TestRowViewModel1实现的是IHierarchy<TestRowViewModel1>,但BucketT<T>要求T必须实现IHierarchy<IBase>。默认泛型接口是不变的,所以IHierarchy<TestRowViewModel1>不能自动转成IHierarchy<IBase>。BucketT<T>本身也是不变的,哪怕TestRowViewModel2实现了IHierarchy<IBase>,BucketT<TestRowViewModel2>也没法转成BucketT<IHierarchy<IBase>>。
具体修改步骤
方案一:用协变接口解决(推荐,保留强类型)
1. 让IHierarchy<T>支持协变
修改IHierarchy<T>的定义,给泛型参数加out关键字,同时把Children的类型从List<T>改成支持协变的IEnumerable<T>:
public interface IHierarchy<out T> : IBase where T : IBase { public IEnumerable<T> Children { get; } }
注:如果必须保留
List<T>,可以在实现类里用显式接口实现,对外暴露IEnumerable<T>:public class TestRowViewModel1 : IHierarchy<TestRowViewModel1>, IBase { public List<TestRowViewModel1> Children => [...]; IEnumerable<TestRowViewModel1> IHierarchy<TestRowViewModel1>.Children => Children; public string BaseContent => [...]; }
2. 让BucketT支持协变
因为类不能直接做协变,所以把BucketT改成协变接口,再用类实现它:
// 定义协变接口 public interface IBucketT<out T> where T : IHierarchy<IBase>, IBase { } // 实现类 public class BucketT<T> : IBucketT<T> where T : IHierarchy<IBase>, IBase { }
3. 调整BucketConfig的BucketList类型
把BucketList的类型改成List<IBucketT<IHierarchy<IBase>>>:
public class BucketConfig { public List<IBucketT<IHierarchy<IBase>>> BucketList { get; } = new List<IBucketT<IHierarchy<IBase>>>(); public BucketConfig() { var test1 = new BucketT<TestRowViewModel1>(); var test2 = new BucketT<TestRowViewModel2>(); this.BucketList.Add(test1); this.BucketList.Add(test2); } }
方案二:放弃强类型约束(简单直接)
如果不想改接口协变,可以创建一个非泛型的基接口,让BucketT<T>继承它:
// 非泛型基接口 public interface IBucket { } public class BucketT<T> : IBucket where T : IHierarchy<T>, IBase // 放宽约束,只要实现IHierarchy<T>即可 { } // 修改BucketConfig的BucketList类型 public class BucketConfig { public List<IBucket> BucketList { get; } = new List<IBucket>(); public BucketConfig() { var test1 = new BucketT<TestRowViewModel1>(); var test2 = new BucketT<TestRowViewModel2>(); this.BucketList.Add(test1); this.BucketList.Add(test2); } }
这种方法不需要修改原有接口的定义,但BucketList里的元素会失去泛型类型信息,后续使用需要强制转换。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

