我可以满足兼容返回类型的接口吗?

时间:2018-01-25 11:40:04

标签: c# covariance

我有以下代码段

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<InventoryItem>与接口成员返回类型System.Collections.Generic.IEnumerable<IInventoryItem>不匹配

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

问题是:如果IEnumerable<InventoryItem>IEnumerable<IInventoryItem>,为什么InventoryItem[]不是Items.get

修改

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

  • IEnumerable<T>将始终返回有效IEnumerable<IInventoryItem>
  • ParserFunctions

3 个答案:

答案 0 :(得分:4)

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

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

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

澄清:

IInventoryItem属性返回Items 如果您添加了一个实现IEnumerable<InventoryItem>的其他类,并且该类未从IInventoryItem继承,则您的属性将无法返回InventoryItem。 假设你添加这个类:

IEnumerable

您当前的public class MyNotRelatedInventoryItem : IInventoryItem { /* implementation here */ } 媒体资源永远无法容纳Items IEnumerable,因为它与您的MyNotRelatedInventoryItem课程无关。

答案 1 :(得分:3)

除了佐哈已经提到过的内容之外,以下是如何用泛型实现你想要的东西:

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 - 方法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 { }

答案 2 :(得分:2)

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

public  IEnumerable<IInventoryItem> Items => items;

而不是

public  IEnumerable<InventoryItem> Items => items;

由于存在从InvetoryItemIInventoryItem的隐式转换,编译器就可以了

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

如果您的界面如下所示:

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

这是不可能的,以下代码

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

会引发以下编译错误:

  

错误CS0266
  无法隐式转换类型&#39; System.Collections.Generic.IEnumerable&#39;到&#39; ConsoleApplication1.InventoryItem []&#39;。 存在显式转换(您是否错过了演员?)

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

你必须像这样明确地转换:

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

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