接口实现上成员的返回类型必须与下层定义完全​​匹配?

根据CSharp语言规范。

接口定义了可以通过类和结构实现的契约。 接口不提供它定义的成员的实现 – 它仅指定必须由实现接口的类或结构提供的成员。

所以我有这个:

interface ITest { IEnumerable Integers { get; set; } } 

我的意思是。 “我与一个属性的合同是一个你可以枚举的整数集合”。

然后我想要以下接口实现:

 class Test : ITest { public List Integers { get; set; } } 

我得到以下编译器错误:

‘Test’没有实现接口成员’ITest.Integers’。 ‘Test.Integers’无法实现’ITest.Integers’,因为它没有匹配的返回类型’System.Collections.Generic.IEnumerable’。

只要我可以说我的Test类实现了ITest契约,因为int属性的List实际上是int的IEnumerable。

那么c#编译器告诉我错误的方式呢?

你不能这样做,因为你手上有一个重大问题,具体取决于实施情况,如果这是允许的话。 考虑:

 interface ITest { IEnumerable Integers { get; set; } } class Test : ITest { // if this were allowed.... public List Integers { get; set; } } 

这将允许:

 ITest test = new Test(); test.Integers = new HashSet(); 

这会使Test的合同无效,因为Test说它包含List

现在,您可以使用显式接口实现来允许它满足这两个签名,具体取决于它是从ITest引用还是Test引用调用:

 class Test : ITest { // satisfies interface explicitly when called from ITest reference IEnumerable ITest.Integers { get { return this.Integers; } set { this.Integers = new List(value); } } // allows you to go directly to List when used from reference of type Test public List Integers { get; set; } } 

仅供参考,您想要的function称为“虚方法返回类型协方差”,正如您所发现的那样,C#不支持它。 它是其他面向对象语言的一个特性,比如C ++。

虽然我们经常收到此function的请求,但我们没有计划将其添加到该语言中。 这不是一个可怕的特征; 如果我们拥有它,我会使用它。 但是我们有很多理由不这样做,包括CLR不支持它,它为可版本化的组件添加了新的和有趣的故障模式,Anders认为它不是一个非常有趣的function,而且我们有很多很多更高的优先级和有限的预算。

顺便说一下,尽管人们一直要求我们使用虚方法返回类型协方差,但是没有人要求虚方法forms参数类型相反 ,即使逻辑上它们本质上是相同的特征。 也就是说,我有一个带有长颈鹿的虚拟方法/接口方法M,我想用一个带动物的方法M来覆盖/实现它。

简单的事实是,如果界面说:

 IInterface{ Animal A { get; } } 

然后,该属性的实现必须与该类型完全匹配 。 试图将其实现为

 MyClass : IInterface{ Duck A { get; } } 

不起作用 – 即使DuckAnimal

相反,你可以这样做:

 MyClass : IInterface{ Duck A { get; } Animal IInterface.A { get { return A; } } } 

即提供IInterface.A成员的显式实现,利用DuckAnimal之间的类型关系。

在你的情况下,这意味着至少实现吸气剂,ITest.Integers为

 IEnumerable ITest.Integers { get { return Integers; } } 

要实现setter,您需要乐观地转换或在输入值上使用.ToList()。

请注意,在这些显式实现中使用AIntegers不是递归的,因为显式接口实现在类型的公共视图中是隐藏的 – 只有当调用者通过它的IInterface / ITest接口实现与该类型对话时它们才会启动。

您需要规范中的13.4.4:

出于接口映射的目的,类成员A匹配接口成员B

AB是属性, AB的名称和类型 相同 ,并且A具有与B相同的访问器( A不是显式接口成员实现,则允许A具有其他访问者)。

另外,您认为List Integers { get; set; } List Integers { get; set; } 满足IEnumerable Integers { get; set; }的合约IEnumerable Integers { get; set; } IEnumerable Integers { get; set; } 是假的。 即使规范以某种方式放宽以不要求返回类型相同,请注意具有公共setter的List类型的属性与具有公共setter的IEnumerable类型的属性不同。因为对于后者你可以分配一个int[]的实例,但对于前者你不能。

你可以这样做:

  interface ITest { IEnumerable Integers { get; set; } } class Test : ITest { public IEnumerable Integers { get; set; } public Test() { Integers = new List(); } } 

因为Test不是ITest。 为什么? 使用ITest,您可以将数组设置为属性Integers。 但你不能用一个测试。 使用.net 4.0,你可以做那样的事情(协方差和反方差),但不完全是这样,它在每种语言中都是不正确的。