C# 实现接口时的协变和逆变
我最近决定刷新我对 C# 基础知识的记忆,所以这可能是微不足道的,但我遇到了以下问题:
StringCollection
was used in .NET v1.0 in order to create a字符串的强类型集合,而不是基于 object
的 ArrayList
(后来通过包含泛型集合进行了增强):
快速浏览一下 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
IList
基本上指定的是,您可以调用Add
并将任何对象作为参数传递,从装箱的Int
到另一个IList< /code> 到
System.DivideByZeroException
。如果您只提供Add( string )
方法,则无法满足此要求,因为您只能添加字符串。换句话说,您将无法调用
StringCollection.Add( new Object() );
,如果接口实现正确,这应该是完全可行的。 :DWhat
IList
basically specifies is that you can callAdd
and pass any object as a parameter, from a boxedInt
to anotherIList
to aSystem.DivideByZeroException
. If you only supply anAdd( 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该行为是由
IList.Add(object)
的显式实现而不是协变/逆变引起的。根据 MSDN 文档,StringCollection 显式实现了IList.Add(object)
;Add(string)
方法是不相关的。实现可能类似于这样:可以观察到这种区别:
附录
上面没有说明为什么要实现此模式。 C# 语言规范指出 [§13.4.1,添加了强调]:
StringCollection 遵循所需的 IList 行为——IList 不保证可以向其中添加任意对象。 StringCollection 做出了更强有力的保证——主要是它只包含字符串。该类包含自己的强类型方法,用于
Add
、Contains
、Item
以及作为标准用例进行访问的其他方法。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 implementsIList.Add(object)
; theAdd(string)
method is unrelated. The implementation may resemble something like this:This distinction can be observed:
Addendum
The above doesn't address why this pattern is implemented. The C# language specification states that [§13.4.1, emphasis added]:
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 aStringCollection
rather than anIList
. But it still functions perfectly well as anIList
, 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).
如果您使用 .net 2.0+,我只会使用泛型:
这应该为您提供您想要的一切。
If you are using .net 2.0+, I would just use generics:
That should give you everything you want.
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 aStream
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.)