C# 为什么必须显式实现此接口?

C# 为什么必须显式实现此接口?,c#,interface,C#,Interface,几年后回到C#,所以我有点生疏了。我遇到了这个(简化的)代码,它让我抓狂 为什么必须显式实现IDataItem.Children属性?正常的子类属性不满足要求吗?毕竟,属性是直接用来满足它的。为什么不含蓄 public interface IDataItem { IEnumerable<string> Children { get; } } public class DataItem : IDataItem { public Collection<stri

几年后回到C#,所以我有点生疏了。我遇到了这个(简化的)代码,它让我抓狂

为什么必须显式实现
IDataItem.Children
属性?正常的
子类
属性不满足要求吗?毕竟,属性是直接用来满足它的。为什么不含蓄

public interface IDataItem {

    IEnumerable<string> Children { get; }
}

public class DataItem : IDataItem {

    public Collection<string> Children { get; } = new Collection<string>();

    // Why doesn't 'Children' above implement this automatically?!
    // After all it's used directly to satisfy the requirement!
    IEnumerable<string> IDataItem.Children => Children;
}
公共接口IDataItem{
IEnumerable子项{get;}
}
公共类数据项:IDataItem{
公共集合子项{get;}=new Collection();
//为什么上面的“儿童”不能自动实现这一点?!
//毕竟它是直接用来满足需求的!
IEnumerable IDataItem.Children=>Children;
}
根据C#来源,以下是
集合的定义:

[System.Runtime.InteropServices.ComVisible(false)]
公共类集合:
System.Collections.Generic.ICollection,
System.Collections.Generic.IEnumerable,在您的示例中,您的“普通”子属性实际上不满足接口要求。类型不同。你能投下它并不重要——它们是不同的

类似的示例(可能更明显)是,如果您使用返回IEnumerable的实际方法实现接口,并尝试从实际类中使用ICollection方法。仍然存在编译时错误


正如@Ben Voigt所说,转换仍然会生成一些代码,如果你想得到它,你需要隐式地添加它。

任何实现接口的类都必须包含与接口指定的签名匹配的方法定义。接口仅定义签名。这样,C#中的接口类似于一个抽象类,其中所有方法都是抽象的

接口可以包含方法、属性、事件、索引器或这四种成员类型的任意组合

下面是关于接口的好文章。


希望它能帮助你进一步理解。

也许这个例子能让你更清楚。我们希望签名精确匹配1,2,不允许替换,尽管类型之间存在任何继承关系

我们不允许写这个:

public interface IDataItem {

    void DoStuff(string value);
}

public class DataItem : IDataItem {

    public void DoStuff(object value) { }
}
您的示例与此相同,只是要求返回类型而不是参数(并且出于明显的原因,采用了缩小而不是扩大的转换)。如果没有,同样的原则也适用。当涉及到匹配签名时,类型必须精确匹配3

你可以要求一种语言来允许这样的事情发生,这样的语言可能存在。但事实是,这些是C#的规则


1涉及泛型和接口/增量门的一些有限支持的外部

2有些人可能会争论签名是否是此处使用的正确词语,因为在这种情况下,返回类型与参数类型、泛型算术等同等重要;在大多数其他情况下,如果有人谈论C#方法签名,他们将显式忽略返回类型,因为他们(显式或隐式)考虑重载规则所说的内容,对于重载,返回类型不是签名的一部分

尽管如此,我还是很高兴在这里使用“签名”这个词。签名在C#规范中没有正式定义,在使用签名的地方,通常会指出签名的哪些部分不应考虑重载

3不要提及如果您的
子对象
方法返回的
结构
碰巧实现了
IEnumerable
,将引发的问题。现在您有了一个方法,该方法返回值类型的值和一个调用者(通过
IDataItem
接口),该调用者希望接收对对象的引用


因此,该方法甚至不能按原样使用。我们必须(在本例中)使用隐藏的装箱转换来实现接口。当C#的这一部分被指定时,我相信,他们试图为您可以轻松编写的代码提供不太多的“隐藏魔法”。

问题已经在这里得到了回答(您必须匹配接口类型),我们可以用一个例子来说明问题。如果这样做有效:

public interface IDataItem {

    IEnumerable<string> Children { get; set; }

    void Wipe();
}

public class DataItem : IDataItem {

    public Collection<string> Children { get; } = new Collection<string>(); 

    public void Wipe() {
        Children.ClearItems();  //Property exists on Collection, but not on IEnumerable
    }
}
公共接口IDataItem{
IEnumerable子项{get;set;}
无效擦拭();
}
公共类数据项:IDataItem{
公共集合子项{get;}=new Collection();
公共图书馆{
Children.ClearItems();//属性存在于集合上,但不存在于IEnumerable上
}
}
然后我们使用这样的代码:

IDataItem x = new DataItem();

//Should be legal, as Queue implements IEnumerable. Note entirely sure
//how this would work, but this is the system you are asking about.
x.Children = new Queue<string>();

x.Wipe();  // Will fail, as Queue does not have a ClearItems method within
IDataItem x=新数据项();
//应该是合法的,因为队列实现IEnumerable
//这将如何工作,但这是你要问的系统。
x、 Children=新队列();
x、 Wipe();//将失败,因为队列中没有ClearItems方法

您的意思是该属性仅可枚举,或者您需要集合类的属性-适当地键入接口。

它不是“直接”使用的,而是隐式转换(向上转换)编译器将转换为非零数量的代码。
正常的Children属性不满足要求吗?
-否。根据我的理解,实现必须完全匹配接口签名,包括返回类型。C不支持返回类型协方差。您不必使用
IDataItem.Children
显式地,
public IEnumerable Children=>new Collection();
就足够了。@Guy:但是他没有办法从类内部将集合对象用作集合:(但是,
Collection
没有直接实现
IEnumerable
,因此它应该满足要求?当然答案是“不,它没有!”但是你能举一个例子说明在什么情况下这样做不是一件好事吗
IDataItem x = new DataItem();

//Should be legal, as Queue implements IEnumerable. Note entirely sure
//how this would work, but this is the system you are asking about.
x.Children = new Queue<string>();

x.Wipe();  // Will fail, as Queue does not have a ClearItems method within