接口实现疑问:为何IEnumerable<InventoryItem>不兼容IEnumerable<IInventoryItem>?
先来看你的代码和报错的核心问题:
你的代码片段:
using System.Collections.Generic; public interface IInventoryItem {} public class InventoryItem : IInventoryItem {} public interface IInventory { IEnumerable<IInventoryItem> Items { get; } } public class Inventory : IInventory { public IEnumerable<InventoryItem> Items => items; // IEnumerable<IInventoryItem> IInventory.Items => items; private InventoryItem[] items = new InventoryItem[0]; }
报错信息:
error CS0738: Inventory 未实现接口成员 IInventory.Items.get,最佳候选实现 Inventory.Items.get 的返回类型 System.Collections.Generic.IEnumerable
与接口成员返回类型 System.Collections.Generic.IEnumerable 不匹配。
核心原因:接口隐式实现的签名匹配规则 vs 泛型协变
你疑惑的点非常关键——虽然InventoryItem[]可以兼容IEnumerable<IInventoryItem>,IEnumerable<InventoryItem>也确实能隐式转换为IEnumerable<IInventoryItem>(因为IEnumerable<T>是标记了out T的协变接口),但这和接口实现的规则是两回事。
C#对接口的隐式实现有严格要求:你在类中定义的成员,必须和接口成员的签名完全一致,包括返回类型。哪怕两个类型是协变兼容的,只要它们不是完全相同的类型,编译器就不会认为你实现了接口的成员。
举个简单的类比:如果接口要求返回object,你在类里返回string,这也会报错——哪怕string是object的子类,因为返回类型不匹配,编译器不会自动帮你做这个转换来满足接口实现。
为什么显式实现能解决问题?
当你用显式实现接口成员时:
IEnumerable<IInventoryItem> IInventory.Items => items;
你相当于告诉编译器:这个成员是专门属于IInventory接口的,它的返回类型严格遵循接口要求。而你类的公共Items属性可以保留自己的返回类型IEnumerable<InventoryItem>——这两个成员是分开的:调用者如果通过Inventory类型访问Items,会拿到IEnumerable<InventoryItem>;如果通过IInventory接口访问,会拿到IEnumerable<IInventoryItem>,而items数组可以完美适配后者。
另一种解决思路:让类的属性返回类型和接口一致
其实你也可以不用显式实现,只要把类里的Items属性返回类型改成和接口完全一致:
public class Inventory : IInventory { public IEnumerable<IInventoryItem> Items => items; private InventoryItem[] items = new InventoryItem[0]; }
这样编译器就会认可这是对接口的隐式实现,因为返回类型完全匹配,而items作为InventoryItem[],本身就可以隐式转换为IEnumerable<IInventoryItem>(数组的协变+IEnumerable<T>的协变)。
总结一下
- 接口隐式实现要求成员的签名(包括返回类型)和接口成员完全一致,仅仅是类型兼容还不够
IEnumerable<InventoryItem>和IEnumerable<IInventoryItem>是协变兼容的不同类型,所以不能用前者隐式实现后者的接口成员- 两种可行的解决方案:要么用显式实现分离类的公共接口和接口要求,要么修改类的属性返回类型和接口保持一致
内容的提问来源于stack exchange,提问作者Steinbitglis

