我最近决定刷新我对C#基础知识的记忆,所以这可能是微不足道的,但我遇到了以下问题:
在.NET v1.0中使用了 StringCollection
来为字符串创建强类型集合,而不是基于object
的{{1}}(后来通过包含泛型集合来增强):
快速浏览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);
我的第一个假设是,由于.NET框架协方差规则,它是可能的。
所以,为了确保,我尝试编写自己的类来实现int Add(string value);
并更改
IList
检索字符串类型而不是对象类型,但令我惊讶的是,在尝试编译项目时,我遇到了编译时错误:
int Add(object value);
任何想法是什么导致了这个?
谢谢!
答案 0 :(得分: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)
}
}
可以观察到这种区别:
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行为--Ilinist不保证可以向其添加任何任意对象。 StringCollection提供更强的保证 - 主要是它只包含字符串。该类包含其自己的Add
,Contains
,Item
的强类型方法,以及标准用例的其他方法,它以StringCollection
而不是{IList
进行访问{1}}。但它仍然可以很好地作为IList
,接受和返回对象,但如果尝试添加不是字符串的项目,则返回错误代码(如IList允许)。
最终,类是否在类中显示(即明确实现)是由类作者自行决定的。对于框架类,显式实现包含在MSDN文档中,但不能作为类成员访问(例如,在自动完成上下文中显示)。
答案 1 :(得分:0)
如果您使用的是.net 2.0+,我会使用泛型:
IList<string> list = new List<string>();
那应该能给你你想要的一切。
答案 2 :(得分:0)
IList.Add(object)
可以接受除字符串以外的参数 - 它可以接受任何类型。因此,如果您声明接口的实现仅接受字符串,则它不再与接口规范匹配,因为现在我无法传递Stream
。
Variance可以使用其他方式:如果接口方法被声明为接受字符串,那么接受对象就没有问题,因为字符串也是对象,因此接口方法的任何输入也会是您的实施的可接受的输入。 (但是,您仍然需要使用接受字符串的方法提供显式接口实现,因为在C#中,接口方法实现与接口方法声明完全匹配。)
答案 3 :(得分:0)
IList
基本上指定的是,您可以调用Add
并将任何对象作为参数传递,从框Int
到另一个IList
再到{{1} }。如果您只提供System.DivideByZeroException
方法,则表示您尚未满足此要求,因为您只能添加字符串。
换句话说,你将无法调用Add( string )
,如果接口正确实现,这应该是完全可行的。 :d