Array 声明:
Array
public abstract class Array : ICloneable, IList, ICollection, IEnumerable {
我想知道为什么不是这样:
public partial class Array<T> : ICloneable, IList<T>, ICollection<T>, IEnumerable<T> {
如果将其声明为泛型类型会出现什么问题?
如果它是泛型类型,我们是否仍需要非泛型类型,或者它可以从中派生Array<T>?如
Array<T>
public partial class Array: Array<object> {
如果数组成为泛型类型会出现什么问题?
回到C#1.0中,他们主要从Java复制了数组的概念。泛型当时不存在,但是创建者认为它们很聪明,并复制了Java数组具有的破碎的协变量数组语义。这意味着您可以在没有编译时错误的情况下进行此类操作(而是运行时错误):
Mammoth[] mammoths = new Mammoth[10]; Animal[] animals = mammoths; // Covariant conversion animals[1] = new Giraffe(); // Run-time exception
在C#2.0中引入了泛型,但没有协变/相反的泛型类型。如果将数组设置为通用数组,则无法将其强制转换Mammoth[]为Animal[],这是您之前可以做的(即使它已损坏)。因此,使数组通用将破坏 很多 代码。
Mammoth[]
Animal[]
仅在C#4.0中,才引入了接口的协变/逆变泛型类型。这样就可以一劳永逸地修复损坏的数组协方差。但是同样,这会破坏很多现有代码。
Array<Mammoth> mammoths = new Array<Mammoth>(10); Array<Animal> animals = mammoths; // Not allowed. IEnumerable<Animals> animals = mammoths; // Covariant conversion
为什么不阵列实现通用IList<T>,ICollection<T>并且IEnumerable<T>接口?
IList<T>
ICollection<T>
IEnumerable<T>
由于运行时招每个数组T[] 不 执行IEnumerable<T>,ICollection<T>并IList<T>自动。1从Array课程文档中:
T[]
一维数组实现的IList<T>,ICollection<T>,IEnumerable<T>,IReadOnlyList<T>和IReadOnlyCollection<T>通用接口。这些实现是在运行时提供给数组的,因此,通用接口不会出现在Array类的声明语法中。
IReadOnlyList<T>
IReadOnlyCollection<T>
您可以使用数组实现的接口的所有成员吗?
否。文档以以下注释继续:
将数组强制转换为这些接口之一时要意识到的关键是,添加,插入或删除元素throw的成员NotSupportedException。
NotSupportedException
这是因为(例如)ICollection<T>有一个Add方法,但是您不能向数组添加任何内容。它将引发异常。这是.NET Framework中早期设计错误的另一个示例,该错误会使您在运行时引发异常:
Add
ICollection<Mammoth> collection = new Mammoth[10]; // Cast to interface type collection.Add(new Mammoth()); // Run-time exception
并且由于ICollection<T>不是协变的(出于明显的原因),您不能这样做:
ICollection<Mammoth> mammoths = new Array<Mammoth>(10); ICollection<Animal> animals = mammoths; // Not allowed
当然,现在也有协变量IReadOnlyCollection<T>接口,该协变量接口也由引擎盖1下的数组实现,但仅包含协变量接口,Count因此用途有限。
Count
如果数组是通用的,我们还需要非通用的Array类吗?
在早期,我们做到了。所有阵列实施非通用IList, ICollection并且 IEnumerable接口,可以通过它们的基类Array。这是为所有数组提供特定方法和接口的唯一合理方法,并且是Array基类的主要用途。您会看到枚举的相同选择:它们是值类型,但继承自Enum;;以及继承自的代表MulticastDelegate。
IList
ICollection
IEnumerable
Enum
MulticastDelegate
Array 现在支持泛型了,可以删除非泛型基类吗?
是的,所有数组共享的方法和接口都可以在通用Array<T>类上定义(如果有)。然后,您可以编写示例,Copy<T>(T[] source, T[] destination)而不是Copy(Array source, Array destination)使用某种类型安全性的附加好处。
Copy<T>(T[] source, T[] destination)
Copy(Array source, Array destination)
但是,从面向对象编程的角度来看,最好有一个通用的非泛型基类Array,该基类可用于引用 任何 数组,而不管其元素的类型如何。就像如何IEnumerable<T>继承IEnumerable(在某些LINQ方法中仍在使用)。
可以在Array基类派生自Array<object>?
Array<object>
不,这将创建循环依赖项:Array<T> : Array : Array<object> : Array : ...。另外,这意味着您可以将 任何 对象存储在数组中(毕竟,所有数组最终都将继承自type Array<object>)。
Array<T> : Array : Array<object> : Array : ...
是否可以在Array<T>不影响现有代码太多的情况下添加新的通用数组类型?
不能。虽然可以使语法适合,但不能使用现有的数组协方差。
数组是.NET中的一种特殊类型。它甚至在通用中间语言中都有自己的说明。如果.NET和C#设计人员决定走这条路,他们可以T[]为其制作语法语法糖Array<T>(就像T?语法糖的Nullable<T>用法一样),并且仍然使用特殊的指令和支持在内存中连续分配数组。
T?
Nullable<T>
但是,你会失去的能力,铸造阵列Mammoth[]到其基本类型之一Animal[],类似于如何可以不投List<Mammoth>给List<Animal>。但是无论如何数组协方差都被破坏了,还有更好的选择。
List<Mammoth>
List<Animal>
数组协方差的替代方案?
所有数组都实现IList<T>。如果将IList<T>接口设为适当的协变接口,则可以将任何数组Array<Mammoth>(或与此相关的任何列表)转换为IList<Animal>。但是,这需要IList<T>重写接口以删除可能更改基础数组的所有方法:
Array<Mammoth>
IList<Animal>
interface IList<out T> : ICollection<T> { T this[int index] { get; } int IndexOf(object value); } interface ICollection<out T> : IEnumerable<T> { int Count { get; } bool Contains(object value); }
(请注意,输入位置上的参数的类型不能T那样,因为这会破坏协方差。但是,object对于Contains和IndexOf来说已经足够了,false当传递不正确类型的对象时,它们会返回。而且实现这些接口的集合可以提供自己的泛型IndexOf(T value)和Contains(T value))
T
object
Contains
IndexOf
false
IndexOf(T value)
Contains(T value)
然后,您可以这样做:
Array<Mammoth> mammoths = new Array<Mammoth>(10); IList<Animals> animals = mammoths; // Covariant conversion
甚至有很小的性能改进,因为在设置数组元素的值时,运行时不必检查分配的值是否与数组元素的实际类型兼容。
我刺探了这种Array<T>类型如何在C#和.NET中实现,并结合上述真正的协变量IList<T>和ICollection<T>接口,将如何工作,并且效果很好。我还添加了不变式IMutableList<T>和IMutableCollection<T>接口,以提供我的new IList<T>和ICollection<T>接口缺乏的突变方法。
IMutableList<T>
IMutableCollection<T>
我围绕它构建了一个简单的集合库,您可以从BitBucket下载源代码和编译的二进制文件,或安装NuGet软件包:
M42.Collections –具有比内置.NET集合类更多的功能,特性和易用性的专业集合。
1)数组T[]中.NET 4.5工具通过它的基类Array:ICloneable,IList,ICollection,IEnumerable,IStructuralComparable,IStructuralEquatable; 默默通过运行时:IList<T>,ICollection<T>,IEnumerable<T>,IReadOnlyList<T>,和IReadOnlyCollection<T>。
ICloneable
IStructuralComparable
IStructuralEquatable