在C#中,所有数组都实现了哪些接口?

87
作为一名新的.NET 3.5程序员,我开始学习LINQ,并发现了一些非常基础的东西,以前从未注意过:
这本书声称每个数组都实现了IEnumerable接口(显然,否则我们无法在数组上使用LINQ to objects...)。当我看到这个时,我想到我从来没有真正思考过这个问题,我问自己:所有的数组还实现了什么?于是我使用对象浏览器检查了System.Array(因为它是CLR中每个数组的基类),令我惊讶的是它并没有实现IEnumerable接口。
所以我的问题是:定义在哪里?我的意思是,我怎样才能准确地知道每个数组实现了哪些接口?
5个回答

85

根据文档(重点标记为我的):

[...] Array类实现System.Collections.Generic.IList<T>System.Collections.Generic.ICollection<T>System.Collections.Generic.IEnumerable<T>泛型接口。这些实现在运行时提供给数组,因此不可见于文档构建工具。

编辑:正如Jb Evain在评论中指出的那样,只有向量(一维数组)实现了泛型接口。至于为什么多维数组没有实现泛型接口,我不太确定,因为它们确实实现了非泛型接口(请参见下面的类声明)。

System.Array类(即每个数组)也实现了这些非泛型接口:

public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable

3
我会这样翻译:继承此非泛型类,因此实现这些非泛型接口。 - abatishchev
我不喜欢问愚蠢的问题,所以我通常会先在MSDN上尝试寻找答案。这一次我错过了...对此感到抱歉并感谢您的帮助。 - ET.
5
我没有点踩,但只有向量(一维数组)类型实现了通用接口。而多维数组类型并没有实现它们。 - Jb Evain
@Jb Evain:好观点!值得注意的是,锯齿数组也可以。 - Hosam Aly
3
锯齿数组其实就是包含其他向量的向量,没有什么奇怪的地方。 - Jb Evain

82

您可以通过一个小的代码片段以经验方式找到问题的答案:

foreach (var type in (new int[0]).GetType().GetInterfaces())
    Console.WriteLine(type);

运行上面的片段将导致以下输出(在.NET 4.0上):

System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]

(`1表示<T>)

在.NET 4.5后(包括.NET Standard 1.0及更高版本),新增了两个接口:

System.Collections.Generic.IReadOnlyList`1[System.Int32]
System.Collections.Generic.IReadOnlyCollection`1[System.Int32]

14
+1,不过我会使用 typeof(int[]) 而不是 (new int[0].GetType()... - Thomas Levesque
1
我喜欢这个。还支持MSDN文档中的引用(将其与迭代typeof(Array).GetType().GetInterfaces()的输出进行比较,其中缺少了通用实现)。 - BoltClock
17
谢谢建议。我更喜欢使用(new int[0]).GetType()来确保结果包括运行时提供给数组的任何实现,正如@BoltClock所提到的。 - Hosam Aly
请注意,在.NET 4.5(.NET Standard 1.0)中,他们还引入了两个接口:IReadOnlyList和IReadOnlyCollection。 - Snak

57

.NET 4.5开始,数组还实现了接口System.Collections.Generic.IReadOnlyList<T>System.Collections.Generic.IReadOnlyCollection<T>

因此,在使用.NET 4.5时,数组实现的接口完整列表如下(使用Hosam Aly's answer中提供的方法获取):

System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]
System.Collections.Generic.IReadOnlyList`1[System.Int32]
System.Collections.Generic.IReadOnlyCollection`1[System.Int32]

奇怪的是,似乎忘记更新MSDN上的文档以提及这两个接口。


1
您的链接文档中包含了这些信息,但有点隐藏: "从.NET Framework 2.0开始,Array类实现了System.Collections.Generic.IList<T>System.Collections.Generic.ICollection<T>System.Collections.Generic.IEnumerable<T>泛型接口。这些实现在运行时提供给数组,因此,在Array类的声明语法中不会出现这些泛型接口。" - Tim Schmelter

1
请注意数组接口,它们可能会实现它们,但实际上它们并没有真正这样做...看一下以下代码:
            var x = new int[] { 1, 2, 3, 4, 5 };
        var y = x as IList<int>;
        Console.WriteLine("The IList:" + string.Join(",", y));
        try
        {
            y.RemoveAt(1);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
        Console.WriteLine(string.Join(",", y));

它会产生以下输出: result 所以解析工作正常,但不是所有内容都被支持,这在固定长度集合的角度来看是正确的,但如果你真的认为它是一个列表,那就非常错误了。这违反了SOLID中的Liskov原则 :(。
对于快速测试,this 会有所帮助。

很棒的观点。我能理解微软的观点,即数组有“Count”并且可索引。但你是对的,它无法实现RemoveAt(x),因为那意味着重新调整数组大小。我猜这是可能的,因为CLR可以移动对变量的引用。-如果没有它,确实Liskov替换原则就不适用了。我想解决方案是将IList分解为IFixedList和IFlexibleList,并仅让Array实现IFixedList。尽管如此,在大多数情况下,我认为类型转换是一种便利。 - Simon Miller
实际上,IList<T> 接口也有一个属性通过 ICollection<T> 叫做 IsReadOnly。如果返回 true,则 IList<T> 实现不需要实际处理操作集合的调用,这对于数组来说是正确的。 - Kasper Cottaar

0

我已经找到了在数组的嵌套类SZArrayHelper中实现的IList<T>, ICollection<T>, IEnumerable<T>

但是我必须警告你 - 那里会有更多的问题...

参考链接

之后我只得到了一个答案 - 没有数组 ;)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接