接口实现上成员的返回类型必须与接口定义完全匹配
本文关键字:接口 定义 实现 成员 返回类型 | 更新日期: 2023-09-27 18:13:08
根据CSharp语言规范。
接口定义了一个可以由类和实现的契约结构体。接口不提供成员的实现它定义——它仅仅指定了必须由实现接口的类或结构。
所以我有这个:
interface ITest
{
IEnumerable<int> Integers { get; set; }
}
我的意思是。"我有一个合约,它的属性是一组可以枚举的整数"
然后我想要以下接口实现:
class Test : ITest
{
public List<int> Integers { get; set; }
}
我得到以下编译错误:
'Test'没有实现接口成员'Test . integers '。的测试。整数"不能实现"ittest。因为它不是整数是否有匹配的返回类型"System.Collections.Generic.IEnumerable"。
只要我能说我的Test类实现了ITest契约因为List of int属性实际上是int的IEnumerable
那么c#编译器是如何告诉我错误的?
供参考,你想要的特性被称为"虚拟方法返回类型协方差",正如你所发现的,c#不支持它。它是其他面向对象语言的一个特性,比如c++。
虽然我们经常收到这个特性的请求,但我们没有计划将它添加到语言中。这并不是一个可怕的特征;如果我们有的话,我会用的。但是我们有很多理由不这样做,包括CLR不支持它,它为可版本化组件添加了新的有趣的故障模式,Anders认为这不是一个非常有趣的特性,而且我们有很多很多更高的优先级和有限的预算。
顺便提一下,尽管人们一直要求我们虚拟方法返回类型协方差,但没有人要求虚拟方法形式参数类型逆变,即使逻辑上它们本质上是相同的特性。也就是说,我有一个虚拟方法/接口方法M,它需要长颈鹿,我想用一个方法M来覆盖它/实现它,它需要一个动物。您不能这样做,因为如果允许这样做的话,您将面临一个主要问题,这取决于实现。考虑:
interface ITest
{
IEnumerable<int> Integers { get; set; }
}
class Test : ITest
{
// if this were allowed....
public List<int> Integers { get; set; }
}
允许:
ITest test = new Test();
test.Integers = new HashSet<int>();
这将使Test的合同无效,因为Test说它包含List<int>
。
现在,可以使用显式接口实现来允许它满足两个签名,这取决于它是从ITest
引用还是从Test
引用调用:
class Test : ITest
{
// satisfies interface explicitly when called from ITest reference
IEnumerable<int> ITest.Integers
{
get
{
return this.Integers;
}
set
{
this.Integers = new List<int>(value);
}
}
// allows you to go directly to List<int> when used from reference of type Test
public List<int> Integers { get; set; }
}
简单的事实是,如果一个界面说:
IInterface{
Animal A { get; }
}
则该属性的实现必须完全匹配类型。尝试将其实现为
MyClass : IInterface{
Duck A { get; }
}
不工作-即使Duck
是Animal
你可以这样做:
MyClass : IInterface{
Duck A { get; }
Animal IInterface.A { get { return A; } }
}
。提供IInterface.A
成员的显式实现,利用Duck
和Animal
之间的类型关系。
在您的例子中,这意味着实现,至少getter, ITest。整数
IEnumerable<int> ITest.Integers { get { return Integers; } }
要实现setter,需要对输入值进行乐观类型强制转换或使用. tolist()。
请注意,在这些显式实现中使用A
和Integers
不是递归的,因为显式接口实现对类型的公共视图是隐藏的——它们只有在调用者通过它的IInterface
/ITest
接口实现与类型对话时才会启动。
您需要规范中的13.4.4:
对于接口映射,类成员
A
在以下情况下匹配接口成员B
:•
A
和B
是属性,A和B的名称和类型是相同的,并且A具有与B
相同的访问器(如果A
不是显式接口成员实现,则允许有额外的访问器)。
另外,你认为List<int> Integers { get; set; }
满足IEnumerable<int> Integers { get; set; }
的合同是错误的。即使规范以某种方式放宽,不要求返回类型相同,请注意,具有公共setter的List<int>
类型的属性与具有公共setter的IEnumerable<int>
类型的属性也不完全相同,因为您可以为后者分配int[]
的实例,但对前者则不能。
你可以这样做:
interface ITest
{
IEnumerable<int> Integers { get; set; }
}
class Test : ITest
{
public IEnumerable<int> Integers { get; set; }
public Test()
{
Integers = new List<int>();
}
}
因为Test不是一个ITest。为什么?使用ITest,您可以将数组设置为属性Integers。但是你不能通过考试。在。net 4.0中,你可以做这样的事情(协方差和反方差),但不完全是这样,这在每种语言中都是不正确的。