实现接口时的C#协方差和逆变

Mik*_* S. 9 .net c# inheritance interface covariance

我最近决定刷新我对C#基础知识的记忆,所以这可能是微不足道的,但我遇到了以下问题:

StringCollection在.NET v1.0中使用,以便为字符串创建强类型集合而不是object基于字符串ArrayList(后来通过包含泛型集合来增强):

快速浏览一下StringCollection定义,您可以看到以下内容:

// Summary:
//     Represents a collection of strings.
[Serializable]
public class StringCollection : IList, ICollection, IEnumerable
{
...
    public int Add(string value);
...
}
Run Code Online (Sandbox Code Playgroud)

你可以看到它实现IList,它包含以下声明(在其他一些声明中):

int Add(object value);
Run Code Online (Sandbox Code Playgroud)

但不是:

int Add(string value);
Run Code Online (Sandbox Code Playgroud)

我的第一个假设是,由于.NET框架协方差规则,它是可能的.

所以,为了确保,我尝试编写自己的实现IList和更改的类

int Add(object value);
Run Code Online (Sandbox Code Playgroud)

检索字符串类型而不是对象类型,但令我惊讶的是,在尝试编译项目时,我遇到了编译时错误:

does not implement interface member 'System.Collections.IList.Add(object)'
Run Code Online (Sandbox Code Playgroud)

有什么想法导致这个?

谢谢!

drf*_*drf 8

这种行为是由明确的实施IList.Add(object)而非共同/逆转引起的.根据MSDN文档,StringCollection显式实现IList.Add(object); 这种Add(string)方法是无关的.实现可能类似于以下内容:

class StringCollection : IList
{
    ...
    public int Add(string value)
    {} // implementation

    public int IList.Add (object value)
    {
        if (!value is string)) return -1;
        return Add(value as string)
    }
}
Run Code Online (Sandbox Code Playgroud)

可以观察到这种区别:

  StringCollection collection = new StringCollection();
  collection.Add(1); // compile error
  (collection as IList).Add(1); // compiles, runtime error
  (collection as IList).Add((object)"") // calls interface method, which adds string to collection
Run Code Online (Sandbox Code Playgroud)

附录

以上并未解释为何实现此模式.C#语言规范指出[§13.4.1,强调增加]:

在某些情况下,接口成员的名称可能不适合实现类,在这种情况下,接口成员可以使用显式接口成员实现来实现.[...]

在方法调用,属性访问或索引器访问中,无法通过其完全限定名称访问显式接口成员实现.显式接口成员实现只能通过接口实例访问,并且在这种情况下仅通过其成员名称引用.

StringCollection遵循所需的IList行为--Ilist不保证可以向其添加任何任意对象.StringCollection提供更强的保证 - 主要是它只包含字符串.类包括它自己的强类型的方法Add,Contains,Item,和其他人在那里作为访问的标准使用情况StringCollection,而不是一个IList.但它仍然可以很好地作为IList接受和返回对象,但如果尝试添加不是字符串的项,则返回错误代码(如IList允许).

最终,类接口是否显示在类中(即明确实现)由类作者自行决定.对于框架类,显式实现包含在MSDN文档中,但不能作为类成员访问(例如,在自动完成上下文中显示).