【问题标题】:Can I satisfy an interface with a compatible return type? [duplicate]我可以满足具有兼容返回类型的接口吗? [复制]
【发布时间】:2018-07-04 15:35:57
【问题描述】:

我有以下sn-p

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];
}

我收到以下错误消息:

错误 CS0738:Inventory 未实现接口成员 IInventory.Items.get 和最佳实现候选 Inventory.Items.get 返回类型 System.Collections.Generic.IEnumerable&lt;InventoryItem&gt; 与接口成员返回类型 System.Collections.Generic.IEnumerable&lt;IInventoryItem&gt; 不匹配

添加IInventory.Items.get 的显式实现完全正确,但为什么有必要?

问题是:如果InventoryItem[] 是IEnumerable&lt;IInventoryItem&gt;,为什么IEnumerable&lt;InventoryItem&gt; 不是IEnumerable&lt;IInventoryItem&gt;?

编辑:

关于为什么这很有趣: 我的假设:

  • Items.get 将始终返回一个有效的 IEnumerable&lt;T&gt; IEnumerable&lt;IInventoryItem&gt;

【问题讨论】:

  • 抛开泛型差异不谈,这个要求总是一直存在:如果一个接口说返回类型是object,你就不能用一个方法来实现它返回string。覆盖时也是如此。
  • 如果要使用具体类型,为什么需要接口?
  • 接口将存在于另一个程序集中,这就是我需要它们的原因。
  • 当有人告诉你你把界面放在任何地方,因为它很好,就会发生这种情况。它最终会被糟糕地使用。

标签: c# covariance


【解决方案1】:

这是必要的,因为IInventoryItem 不(也不能)仅限于由InventoryItem 实施。

如果您(或其他任何人)将编写另一个实现IInventoryItem 接口的类,那么任何实现IInventory 接口的类都可以处理它,因为它只处理IInventoryItem 接口并且不'不知道也不关心具体的实现。

如果编译器允许像您的问题那样编写 Inventory 类,那么它将无法处理任何其他实现 IInventoryItem 接口的类。

澄清:

Items 属性返回 IEnumerable&lt;InventoryItem&gt;。
如果您添加了实现IInventoryItem 的不同类,并且该类没有从InventoryItem 继承,则您的属性将无法返回它的IEnumerable。
假设你添加了这个类:

public class MyNotRelatedInventoryItem : IInventoryItem
{ /* implementation here */ }

您当前的Items 属性将永远无法拥有MyNotRelatedInventoryItem 的IEnumerable,因为它与您的InventoryItem 类没有任何关系。

【讨论】:

  • 一个赞成票,两个反对票。我很想知道我的回答有什么问题...
  • 我没有投反对票,但您似乎认为某些接口知道实现,但他们不知道。
  • 这并没有回答 OP 的问题,即他为什么不能在 get 中返回实现所需接口的具体类型。
  • 您能否举例说明最后一句中的“句柄”是什么意思? If the compiler allowed to write the Inventory class like in your question, then it would not be able to handle any other class that implements the IInventoryItem interface.
  • 真正的原因在作为重复链接的问题的答案中给出:它似乎是 CLR 的限制。
【解决方案2】:

除了 Zohar 已经提到的内容之外,以下是如何使用泛型实现您想要的:

public interface IInventoryItem {}
public class      InventoryItem : IInventoryItem {}
public interface IInventory<T> where T : IInventoryItem
{
    IEnumerable<T> Items { get; } 
}

public class Inventory : IInventory<InventoryItem>
{
    public  IEnumerable<InventoryItem>  Items            => items;    
    private InventoryItem[] items = new InventoryItem[0];
}

但是这有一个缺点,整个接口IInventory 是通用的,因为属性不能是通用的。为了避免这种情况,您可以创建一个 GetItems-method instea,它可以是通用的。

当您拥有一个接口时,您必须使用与该接口定义的完全相同的签名来实现它。出于同样的原因,您不能执行以下操作:

interface MyInterface
{
    A A { get; }
}
class A : MyInterface
{
    public B A { get; private set; } // this will NOT implement the interface, although B derives from A
}
class B : A { }

【讨论】:

    【解决方案3】:

    您的属性返回类型与接口的不匹配,因此编译器不会将其视为接口实现。所以你需要把它改成

    public  IEnumerable<IInventoryItem> Items => items;
    

    而不是

    public  IEnumerable<InventoryItem> Items => items;
    

    由于存在从InvetoryItem到IInventoryItem的隐式转换,所以编译器OK

    这是可能的,因为您只返回值,并且编译器知道数组中的每个 InvetoryItem 都可以转换为 IInventoryItem 类型而不会引发异常。

    如果你的界面是这样的:

    public interface IInventory
    {
        IEnumerable<IInventoryItem> Items { get; set; }
    }
    

    这是不可能的,下面的代码

    public IEnumerable<IInventoryItem> Items
    {
        get { return items; }
        set { items = value; }
    }
    

    会引发以下编译错误:

    错误 CS0266
    无法将类型“System.Collections.Generic.IEnumerable”隐式转换为“ConsoleApplication1.InventoryItem[]”。 存在显式转换(您是否缺少演员表?)

    这意味着编译器无法将任何IInventoryItem 隐式转换为InventoryItem,因为它无法确保此转换始终成功。

    你必须像这样显式转换:

    public IEnumerable<IInventoryItem> Items
    {
        get { return items; }
        set { items = (InventoryItem[])value; }
    }
    

    但如果数组不完全由 InvetoryItem 对象组成,则会发生异常。

    【讨论】:

    • setter不在界面中时是否相关?
    • @Steinbitglis 不是真的,只是为了说明输出的隐式转换和输入的显式转换。
    • 挑剔:属性不是函数,方法的返回类型不是其签名的一部分。
    猜你喜欢
    • 2020-02-19
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    • 2022-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    相关资源
    最近更新 更多