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

发布于 2024-11-29 11:27:07 字数 959 浏览 2 评论 0原文

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

StringCollection was used in .NET v1.0 in order to create a字符串的强类型集合,而不是基于 objectArrayList(后来通过包含泛型集合进行了增强):

快速浏览一下 StringCollection定义,你可以请参阅以下内容:

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

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

int Add(object value);

但不是:

int Add(string value);

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

因此,为了确保这一点,我尝试编写自己的类来实现 IList 并更改

int Add(object value);

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

does not implement interface member 'System.Collections.IList.Add(object)'

有什么想法导致此错误吗?

谢谢!

I've recently decided to refresh my memory regarding C# basics, so this might be trivial, but i've bumped into the following issue:

StringCollection was used in .NET v1.0 in order to create a strongly typed collection for strings as opposed to an object based ArrayList (this was later enhanced by including Generic collections):

Taking a quick glance at StringCollection definition, you can see the following:

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

You can see it implements IList, which contains the following declaration (among a few other declarations):

int Add(object value);

But not:

int Add(string value);

My first assumption was that it is possible due to the .NET framework covariance rules.

So just to make sure, I tried writing my own class which implements IList and changed

int Add(object value);

to retrieve a string type instead of an object type, but for my surprise, when trying to compile the project, I got an compile-time error:

does not implement interface member 'System.Collections.IList.Add(object)'

Any ideas what causes this?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

素年丶 2024-12-06 11:27:08

IList 基本上指定的是,您可以调用 Add 并将任何对象作为参数传递,从装箱的 Int 到另一个 IList< /code> 到 System.DivideByZeroException。如果您只提供 Add( string ) 方法,则无法满足此要求,因为您只能添加字符串。

换句话说,您将无法调用 StringCollection.Add( new Object() );,如果接口实现正确,这应该是完全可行的。 :D

What IList basically specifies is that you can call Add and pass any object as a parameter, from a boxed Int to another IList to a System.DivideByZeroException. If you only supply an Add( string ) method, you haven't fulfilled this requirement since you can only add strings.

In other words, you wouldn't be able to call StringCollection.Add( new Object() );, which should be perfectly doable if the interface was implemented properly. :D

笛声青案梦长安 2024-12-06 11:27:07

该行为是由 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)
    }
}

可以观察到这种区别:

  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

附录

上面没有说明为什么要实现此模式。 C# 语言规范指出 [§13.4.1,添加了强调]:

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

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

StringCollection 遵循所需的 IList 行为——IList 不保证可以向其中添加任意对象。 StringCollection 做出了更强有力的保证——主要是它只包含字符串。该类包含自己的强类型方法,用于 AddContainsItem 以及作为标准用例进行访问的其他方法。 StringCollection 而不是 IList。但它仍然可以像 IList 一样完美地工作,接受并返回对象,但如果尝试添加不是字符串的项目,则返回错误代码(如 IList 允许)。

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

The behavior is caused by the explicit implementation of IList.Add(object) rather than co/contravariance. Per the MSDN documentation, StringCollection explicitly implements IList.Add(object); the Add(string) method is unrelated. The implementation may resemble something like this:

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)
    }
}

This distinction can be observed:

  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

Addendum

The above doesn't address why this pattern is implemented. The C# language specification states that [§13.4.1, emphasis added]:

In some cases, the name of an interface member may not be appropriate
for the implementing class, in which case the interface member may be
implemented using explicit interface member implementation. [...]

It is not possible to access an explicit interface member implementation through its fully qualified name in a method invocation, property access, or indexer access. An explicit interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name.

StringCollection adheres to the required IList behavior -- IList makes no guarantee that any arbitrary object can be added to it. StringCollection makes stronger guarantees -- primarily, that it will contain only strings. The class includes its own strongly-typed methods for Add, Contains, Item, and others for the standard use case where it is accessed as a StringCollection rather than an IList. But it still functions perfectly well as an IList, accepting and returning objects, but returning an error code (as IList permits) if an attempt is made to add an item that is not a string.

Ultimately, whether an interface shows up in the class (i.e., is explicitly implemented) is at the discretion of the class author. In the case of framework classes, explicit implemenentations are included in the MSDN documentation but are not accessible as class members (e.g., shown in autocompletion contexts).

酒绊 2024-12-06 11:27:07

如果您使用 .net 2.0+,我只会使用泛型:

IList<string> list = new List<string>();

这应该为您提供您想要的一切。

If you are using .net 2.0+, I would just use generics:

IList<string> list = new List<string>();

That should give you everything you want.

感受沵的脚步 2024-12-06 11:27:07

IList.Add(object) 可以接受字符串以外的参数——它可以接受任何类型。因此,如果您将接口的实现声明为仅接受字符串,则它不再符合接口规范,因为现在我无法传入 Stream 等。

变体可以以另一种方式工作:如果接口方法被声明为接受字符串,那么接受对象就可以了,因为字符串也是对象,因此接口方法的任何输入也都是可接受的输入到您的实施。 (但是,您仍然必须提供带有接受字符串的方法的显式接口实现,因为在 C# 中,接口方法实现与接口方法声明非常匹配。)

IList.Add(object) can accept parameters other than strings -- it can accept any type. So if you declare your implementation of the interface to only accept strings, it no longer matches the interface specification, because now I couldn't pass in a Stream for example.

Variance can work the other way: if the interface method was declared to accept strings, then accepting objects would be alright, since strings are also objects, and therefore any input to the interface's method would also be acceptable input to your implementation. (However, you would still have to provide an explicit interface implementation with a method accepting string, because in C# an interface method implementation much exactly match the interface method declaration.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文