几年后回到C#所以我有点生疏了.遇到这个(简化的)代码,它让我头疼.
为什么必须明确实现该IDataItem.Children
属性?普通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;
}
Run Code Online (Sandbox Code Playgroud)
根据C#源代码,这里的定义是Collection<T>
:
[System.Runtime.InteropServices.ComVisible(false)]
public class Collection<T> :
System.Collections.Generic.ICollection<T>,
System.Collections.Generic.IEnumerable<T>, <-- Right Here
System.Collections.Generic.IList<T>,
System.Collections.Generic.IReadOnlyCollection<T>,
System.Collections.Generic.IReadOnlyList<T>,
System.Collections.IList
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,它明确地实现了,IEnumerable<T>
并且从我的理解,如果'X'实现'Y'然后'X' 是 'Y',所以Collection<String>
是一个IEnumerable<String>
所以我不确定为什么它不满意.
也许这个例子让它更清晰.我们希望签名完全匹配1,2,不允许任何替换,尽管类型之间存在任何继承关系.
我们不允许写这个:
public interface IDataItem {
void DoStuff(string value);
}
public class DataItem : IDataItem {
public void DoStuff(object value) { }
}
Run Code Online (Sandbox Code Playgroud)
你的例子是相同的,除了你要求返回类型而不是参数(并采用缩小而不是加宽转换,原因很明显).尽管如此,同样的原则也适用.在匹配签名时,类型必须完全匹配3.
您可以要求提供允许此类事件发生的语言,并且可能存在此类语言.但事实是,这些是C#的规则.
1除了涉及泛型和接口/ delgates的Co和Contra-variance的一些有限支持之外.
2有些人可能会争论签名是否是在这里使用的正确词汇,因为在这种情况下,返回类型与参数类型,通用arity等一样重要; 在大多数其他人谈论C#方法签名的情况下,他们将明确忽略返回类型,因为他们(明确地或隐含地)考虑重载规则所说的内容,并且对于重载,返回类型不是签名的一部分.
尽管如此,我对我在这里使用"签名"一词感到满意.签名未在C#规范中正式定义,并且在使用它的地方,通常会指出签名的哪些部分不被考虑用于重载.
3更不用说如果你的Children
方法返回了一个struct
实现的问题,那么会引发的问题IEnumerable<string>
.现在你有了一个返回值类型值和调用者的方法(通过IDataItem
希望接收对象引用的接口).
因此,该方法甚至不能按原样使用.我们必须(在这种情况下)隐藏装箱转换来实现接口.当推测C#的这一部分时,我相信它们不会为你自己可以轻易编写的代码提供太多"隐藏的魔力".
在您的示例中,您的"普通"Children属性实际上并不满足接口要求.类型不同.你可以施展它并不重要 - 它们是不同的.
类似的例子,也许更明显的是,如果你将实现一个接口,其实际方法返回IEnumerable并尝试从实际类中的ICollection方法.仍然存在编译时错误.
正如@Ben Voigt所说,转换仍会产生一些代码,如果你想拥有它 - 你需要隐式添加它.