如果数组成为泛型类型会出现什么问题?
回到C#1.0中,他们主要从Java复制了数组的概念。泛型当时不存在,但是创建者认为它们很聪明,并复制了Java数组具有的破碎的协变量数组语义。这意味着您可以在没有编译时错误的情况下进行此类 *** 作(而是运行时错误):
Mammoth[] mammoths = new Mammoth[10];Animal[] animals = mammoths; // Covariant conversionanimals[1] = new Giraffe(); // Run-time exception
在C#2.0中引入了泛型,但没有协变/相反的泛型类型。如果将数组设置为通用数组,则无法将其强制转换
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>接口?
由于运行时招每个数组
T[]不
执行
IEnumerable<T>,
ICollection<T>并
IList<T>自动。1从
Array课程文档中:
一维数组实现的
IList<T>,ICollection<T>,IEnumerable<T>,IReadOnlyList<T>和IReadOnlyCollection<T>通用接口。这些实现是在运行时提供给数组的,因此,通用接口不会出现在Array类的声明语法中。
您可以使用数组实现的接口的所有成员吗?
否。文档以以下注释继续:
将数组强制转换为这些接口之一时要意识到的关键是,添加,插入或删除元素throw的成员
NotSupportedException。
这是因为(例如)
ICollection<T>有一个
Add方法,但是您不能向数组添加任何内容。它将引发异常。这是.NET
framework中早期设计错误的另一个示例,该错误会使您在运行时引发异常:
ICollection<Mammoth> collection = new Mammoth[10]; // Cast to interface typecollection.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因此用途有限。
基类
Array
如果数组是通用的,我们还需要非通用的
Array类吗?
在早期,我们做到了。所有阵列实施非通用
IList,
ICollection并且
IEnumerable接口,可以通过它们的基类
Array。这是为所有数组提供特定方法和接口的唯一合理方法,并且是
Array基类的主要用途。您会看到枚举的相同选择:它们是值类型,但继承自
Enum;;以及继承自的代表
MulticastDelegate。
Array现在支持泛型了,可以删除非泛型基类吗?
是的,所有数组共享的方法和接口都可以在通用
Array<T>类上定义(如果有)。然后,您可以编写示例,
Copy<T>(T[] source, T[]destination)而不是
Copy(Array source, Array destination)使用某种类型安全性的附加好处。
但是,从面向对象编程的角度来看,最好有一个通用的非泛型基类
Array,该基类可用于引用 任何
数组,而不管其元素的类型如何。就像如何
IEnumerable<T>继承
IEnumerable(在某些LINQ方法中仍在使用)。
可以在
Array基类派生自Array<object>?
不,这将创建循环依赖项:
Array<T> : Array : Array<object> : Array : ...。另外,这意味着您可以将 任何
对象存储在数组中(毕竟,所有数组最终都将继承自type
Array<object>)。
未来
是否可以在
Array<T>不影响现有代码太多的情况下添加新的通用数组类型?
不能。虽然可以使语法适合,但不能使用现有的数组协方差。
数组是.NET中的一种特殊类型。它甚至在通用中间语言中都有自己的说明。如果.NET和C#设计人员决定走这条路,他们可以
T[]为其制作语法语法糖
Array<T>(就像
T?语法糖的
Nullable<T>用法一样),并且仍然使用特殊的指令和支持在内存中连续分配数组。
但是,你会失去的能力,铸造阵列
Mammoth[]到其基本类型之一
Animal[],类似于如何可以不投
List<Mammoth>给
List<Animal>。但是无论如何数组协方差都被破坏了,还有更好的选择。
数组协方差的替代方案?
所有数组都实现
IList<T>。如果将
IList<T>接口设为适当的协变接口,则可以将任何数组
Array<Mammoth>(或与此相关的任何列表)转换为
IList<Animal>。但是,这需要
IList<T>重写接口以删除可能更改基础数组的所有方法:
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(Tvalue)和
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>接口缺乏的突变方法。
我围绕它构建了一个简单的集合库,您可以从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>。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)