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

Jua*_*gui 9 c#

根据CSharp语言规范.

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

所以我有这个:

interface ITest
{
    IEnumerable<int> Integers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

class Test : ITest
{
    public List<int> Integers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我得到以下编译器错误:

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

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

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

Eri*_*ert 19

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

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

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

  • `virtual method formal parameter type contravariance`是太多的单词.人们只会问他们可以轻松描述的事情:) (5认同)
  • @RomanR.我认为他的意思是[Anders Hejlsberg](http://www.microsoft.com/presspass/exec/techfellow/hejlsberg/default.mspx) (3认同)
  • 我可以问安德斯是谁? (2认同)
  • @Jason:在给定的示例中,您是正确的;无法安全地将get / set属性的类型设为协变。但是,如果只有吸气剂,并且已知其返回类型是引用类型,则可以出于实现目的将其安全地设为协变。(同样,如果它是引用类型的set-only属性,则可以放心地将其设为反变。)如果我们支持该功能,则不支持。 (2认同)
  • @fuglede:我已经在C#团队工作了四年有余;在GitHub上要求他们! (2认同)

Jam*_*are 9

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

interface ITest
{
    IEnumerable<int> Integers { get; set; }
}

class Test : ITest
{
    // if this were allowed....
    public List<int> Integers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这将允许:

ITest test = new Test();
test.Integers = new HashSet<int>();
Run Code Online (Sandbox Code Playgroud)

这会使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; }
}
Run Code Online (Sandbox Code Playgroud)


And*_*tan 5

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

IInterface{
   Animal A { get; }
}
Run Code Online (Sandbox Code Playgroud)

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

MyClass : IInterface{
  Duck A { get; }
}
Run Code Online (Sandbox Code Playgroud)

不起作用 - 即使Duck是一个Animal

相反,你可以这样做:

MyClass : IInterface{
  Duck A { get; }
  Animal IInterface.A { get { return A; } }
}
Run Code Online (Sandbox Code Playgroud)

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

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

IEnumerable<int> ITest.Integers { get { return Integers; } }
Run Code Online (Sandbox Code Playgroud)

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

请注意,这些显式实现的使用AIntegers内部不是递归的,因为显式接口实现在类型的公共视图中是隐藏的 - 它们仅在调用者通过它的IInterface/ ITest接口实现与类型进行通信时启动.