C# 为什么 Array 不是泛型类型?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14324987/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 11:27:25  来源:igfitidea点击:

Why isn't Array a generic type?

c#genericstypeslanguage-design

提问by Ken Kin

Arrayis declared:

Array声明:

public abstract class Array
    : ICloneable, IList, ICollection, IEnumerable {

I'm wondering why isn't it:

我想知道为什么不是:

public partial class Array<T>
    : ICloneable, IList<T>, ICollection<T>, IEnumerable<T> {
  1. What would be the issue if it was declared as a generic type?

  2. If it was a generic type, do we still need the non-generic one or could it derive from Array<T>? Such as

    public partial class Array: Array<object> { 
    
  1. 如果将其声明为泛型类型会出现什么问题?

  2. 如果它是泛型类型,我们还需要非泛型类型还是可以从它派生Array<T>?如

    public partial class Array: Array<object> { 
    

采纳答案by Daniel A.A. Pelsmaeker

History

历史

What problems would arise if arrays became a generic type?

如果数组成为泛型类型会出现什么问题?

Back in C# 1.0 they copied the concept of arrays mainly from Java. Generics did not exist back then, but the creators thought they were smart and copied the broken covariant array semantics that Java arrays have. This means that you can pull off things like this without a compile-time error (but a runtime-error instead):

在 C# 1.0 中,他们主要从 Java 中复制了数组的概念。那时泛型并不存在,但创建者认为他们很聪明,并复制了 Java 数组所具有的损坏的协变数组语义。这意味着你可以在没有编译时错误的情况下完成这样的事情(而是一个运行时错误):

Mammoth[] mammoths = new Mammoth[10];
Animal[] animals = mammoths;            // Covariant conversion
animals[1] = new Giraffe();             // Run-time exception

In C# 2.0 generics were introduced, but no covariant/contravariant generic types. If arrays were made generic, then you couldn't cast Mammoth[]to Animal[], something you could do before (even though it was broken). So making arrays generic would've broken a lotof code.

在 C# 2.0 中引入了泛型,但没有协变/逆变泛型类型。如果数组是通用的,那么你就不能强制转换Mammoth[]Animal[],你以前可以做的事情(即使它被破坏了)。因此,使数组通用会破坏很多代码。

Only in C# 4.0 were covariant/contravariant generic types for interfaces introduced. This made it possible to fix the broken array covariance once and for all. But again, this would've broken a lot of existing code.

只有在 C# 4.0 中才引入了接口的协变/逆变泛型类型。这使得可以一劳永逸地修复损坏的数组协方差。但同样,这会破坏很多现有的代码。

Array<Mammoth> mammoths = new Array<Mammoth>(10);
Array<Animal> animals = mammoths;           // Not allowed.
IEnumerable<Animals> animals = mammoths;    // Covariant conversion


Arrays implement generic interfaces

数组实现通用接口

Why don't arrays implement the generic IList<T>, ICollection<T>and IEnumerable<T>interfaces?

为什么不阵列实现通用IList<T>ICollection<T>并且IEnumerable<T>接口?

Thanks to a runtime trick every array T[]doesimplement IEnumerable<T>, ICollection<T>and IList<T>automatically.1From the Arrayclass documentation:

由于运行时招每个数组T[]执行IEnumerable<T>ICollection<T>IList<T>自动。1Array类文档

Single-dimensional arrays implement the IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyList<T>and IReadOnlyCollection<T>generic interfaces. The implementations are provided to arrays at run time, and as a result, the generic interfaces do not appear in the declaration syntax for the Array class.

一维数组实现的IList<T>ICollection<T>IEnumerable<T>IReadOnlyList<T>IReadOnlyCollection<T>通用接口。实现在运行时提供给数组,因此,泛型接口不会出现在 Array 类的声明语法中。



Can you use all members of the interfaces implemented by arrays?

你能使用数组实现的接口的所有成员吗?

No. The documentation continues with this remark:

不。文档继续这样评论:

The key thing to be aware of when you cast an array to one of these interfaces is that members which add, insert, or remove elements throw NotSupportedException.

将数组转换为这些接口之一时要注意的关键是添加、插入或删除元素的成员 throw NotSupportedException

That's because (for example) ICollection<T>has an Addmethod, but you cannot add anything to an array. It will throw an exception. This is another example of an early design error in the .NET Framework that will get you exceptions thrown at you at run-time:

那是因为(例如)ICollection<T>有一个Add方法,但您不能向数组添加任何内容。它会抛出异常。这是 .NET Framework 中早期设计错误的另一个示例,它将在运行时向您抛出异常:

ICollection<Mammoth> collection = new Mammoth[10];  // Cast to interface type
collection.Add(new Mammoth());                      // Run-time exception

And since ICollection<T>is not covariant (for obvious reasons), you can't do this:

而且由于ICollection<T>不是协变的(出于明显的原因),您不能这样做:

ICollection<Mammoth> mammoths = new Array<Mammoth>(10);
ICollection<Animal> animals = mammoths;     // Not allowed

Of course there is now the covariant IReadOnlyCollection<T>interfacethat is also implemented by arrays under the hood1, but it contains only Countso it has limited uses.

当然,现在还有协变IReadOnlyCollection<T>接口,它也由引擎盖下的数组实现1,但它仅包含Count因此用途有限。



The base class Array

基类 Array

If arrays were generic, would we still need the non-generic Arrayclass?

如果数组是泛型的,我们还需要非泛型Array类吗?

In the early days we did. All arrays implement the non-generic IList, ICollectionand IEnumerableinterfaces through their base class Array. This was the only reasonable way to give all arrays specific methods and interfaces, and is the primary use of the Arraybase class. You see the same choice for enums: they are value types but inherit members from Enum; and delegates that inherit from MulticastDelegate.

在早期,我们做到了。所有阵列实施非通用IListICollection并且 IEnumerable接口,可以通过它们的基类Array。这是为所有数组提供特定方法和接口的唯一合理方法,并且是Array基类的主要用途。您会看到枚举的相同选择:它们是值类型,但从Enum;继承成员;和继承自MulticastDelegate.

Could the non-generic base class Arraybe removed now that generics are supported?

Array既然支持泛型,是否可以删除非泛型基类?

Yes, the methods and interfaces shared by all arrays could be defined on the generic Array<T>class if it ever came into existence. And then you could write, for example, Copy<T>(T[] source, T[] destination)instead of Copy(Array source, Array destination)with the added benefit of some type safety.

是的,所有数组共享的方法和接口都可以在泛型Array<T>类上定义,如果它出现的话。然后你可以写,例如,Copy<T>(T[] source, T[] destination)而不是Copy(Array source, Array destination)一些类型安全的额外好处。

However, from an Object-Oriented Programming point of view it is nice to have a common non-generic base class Arraythat can be used to refer to anyarray regardless of the type of its elements. Just like how IEnumerable<T>inherits from IEnumerable(which is still used in some LINQ methods).

然而,从面向对象编程的角度来看,拥有一个通用的非泛型基类是很好的,Array它可以用来引用任何数组,而不管其元素的类型。就像IEnumerable<T>继承自IEnumerable(在某些 LINQ 方法中仍然使用)一样。

Could the Arraybase class derive from Array<object>?

可以在Array基类派生自Array<object>

No, that would create a circular dependency: Array<T> : Array : Array<object> : Array : .... Also, that would imply you could store anyobject in an array (after all, all arrays would ultimately inherit from type Array<object>).

不,这会产生循环依赖:Array<T> : Array : Array<object> : Array : .... 此外,这意味着您可以将任何对象存储在数组中(毕竟,所有数组最终都会从 type 继承Array<object>)。



The future

未来

Could the new generic array type Array<T>be added without impacting existing code too much?

是否可以在Array<T>不过多影响现有代码的情况下添加新的通用数组类型?

No. While the syntax could be made to fit, the existing array covariance could not be used.

不可以。虽然可以使语法适合,但不能使用现有的数组协方差。

An array is a special type in .NET. It even has its own instructions in the Common Intermediate Language. If the .NET and C# designers ever decide to go down this road, they could make the T[]syntax syntactic sugar for Array<T>(just like how T?is syntactic sugar for Nullable<T>), and still use the special instructions and support that allocates arrays contiguously in memory.

数组是 .NET 中的一种特殊类型。它甚至有自己的通用中间语言指令。如果 .NET 和 C# 设计者决定走这条路,他们可以制作T[]for的语法语法糖Array<T>(就像T?语法糖 for 一样Nullable<T>),并且仍然使用在内存中连续分配数组的特殊指令和支持。

However, you would lose the ability to cast arrays of Mammoth[]to one of their base types Animal[], similar to how you can't cast List<Mammoth>to List<Animal>. But array covariance is broken anyway, and there are better alternatives.

但是,您将无法将 的数组Mammoth[]转换为它们的基本类型之一Animal[],类似于您无法转换List<Mammoth>为 的方式List<Animal>。但是数组协方差无论如何都被破坏了,并且有更好的选择。

Alternatives to array covariance?

数组协方差的替代方法?

All arrays implement IList<T>. If the IList<T>interface were made into a proper covariant interface then you could cast any array Array<Mammoth>(or any list for that matter) to an IList<Animal>. However, this requires the IList<T>interface to be rewritten to remove all methods that might change the underlying array:

所有数组都实现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);
}

(Note that the types of parameters on input positions cannot be Tas this would break covariance. However, objectis good enough for Containsand IndexOf, who would just return falsewhen passed an object of an incorrect type. And collections implementing these interfaces can provide their own generic IndexOf(T value)and Contains(T value).)

(请注意,输入位置上的参数类型不能是,T因为这会破坏协方差。然而,object对于ContainsandIndexOf来说已经足够了,false当传递一个错误类型的对象时,他们只会返回。实现这些接口的集合可以提供自己的泛型IndexOf(T value)Contains(T value).)

Then you could do this:

那么你可以这样做:

Array<Mammoth> mammoths = new Array<Mammoth>(10);
IList<Animals> animals = mammoths;    // Covariant conversion

There is even a small performance improvement because the runtime would not have to check whether an assigned value is type compatible with the real type of the array's elements when setting the value of an element of an array.

甚至还有很小的性能改进,因为运行时在设置数组元素的值时不必检查分配的值是否与数组元素的实际类型兼容。



My stab at it

我的刺

I took a stab at how such an Array<T>type would work if it were implemented in C# and .NET, combined with the real covariant IList<T>and ICollection<T>interfaces described above, and it works quite nicely. I also added the invariant IMutableList<T>and IMutableCollection<T>interfaces to provide the mutation methods that my new IList<T>and ICollection<T>interfaces lack.

Array<T>如果这种类型在 C# 和 .NET 中实现,并结合上面描述的真正的协变IList<T>ICollection<T>接口,我尝试了它的工作方式,并且它工作得非常好。我还添加了不变式IMutableList<T>IMutableCollection<T>接口以提供我的 newIList<T>ICollection<T>接口缺少的变异方法。

I built a simple collection library around it, and you can download the source code and compiled binaries from BitBucket, or install the NuGet package:

我围绕它构建了一个简单的集合库,您可以从 BitBucket 下载源代码和编译后的二进制文件,或者安装 NuGet 包:

M42.Collections– Specialized collections with more functionality, features and ease-of-use than the built-in .NET collection classes.

M42.Collections– 比内置的 .NET 集合类具有更多功能、特性和易用性的专用集合。



1) An array T[]in .Net 4.5 implements through its base class Array: ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable; and silently through the runtime: IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyList<T>, and IReadOnlyCollection<T>.

1) T[].Net 4.5 中的数组通过其基类实现ArrayICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable; 并默默地通过运行时:IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyList<T>, 和IReadOnlyCollection<T>

回答by TomTom

Compatibility. Array is a historic type that goes back to the time that there were no generics.

兼容性。Array 是一种历史类型,可以追溯到没有泛型的时代。

Today it would make sense to have Array, then Array<T>, then the specific class ;)

今天,有Array,然后Array<T>,然后是特定的类是有意义的;)

回答by JLRishe

Thus I'd like to know why it is not:

因此,我想知道为什么不是:

The reason is that generics were not present in the first version of C#.

原因是在 C# 的第一个版本中不存在泛型。

But I cannot figure out what would be the problem myself.

但我无法弄清楚自己会出现什么问题。

The problem is that it would break a huge amount of code that uses the Arrayclass. C# doesn't support multiple inheritance, so lines like this

问题是它会破坏大量使用Array该类的代码。C# 不支持多重继承,所以像这样的行

Array ary = Array.Copy(.....);
int[] values = (int[])ary;

would be broken.

会被打破。

If MS were making C# and .NET all over again from scratch, then there probably would be no problem in making Arraya generic class, but that is not the reality.

如果 MS 从头开始​​重新制作 C# 和 .NET,那么制作Array泛型类可能没有问题,但事实并非如此。

回答by atlaste

[Update, new insights, it felt something was missing until now]

[更新,新的见解,直到现在感觉还缺少一些东西]

Regarding the earlier answer:

关于之前的回答:

  • Arrays are covariant like other types can be. You can implement things like 'object[] foo = new string[5];' with covariance, so that is not the reason.
  • Compatibility is probably the reason for not reconsidering the design, but I argue this is also not the correct answer.
  • 数组是协变的,就像其他类型一样。你可以实现像'object[] foo = new string[5];'这样的东西 协方差,所以这不是原因。
  • 兼容性可能是不重新考虑设计的原因,但我认为这也不是正确的答案。

However, the other reason I can think of is because an array is the 'basic type' for a linear set of elements in memory. I've been thinking about using Array<T>, which is where you might also wonder why T is an Object and why this 'Object' even exists? In this scenario T[] is just what I consider another syntax for Array<T> which is covariant with Array. Since the types actually differ, I consider the two cases similar.

但是,我能想到的另一个原因是数组是内存中一组线性元素的“基本类型”。我一直在考虑使用 Array<T>,这也是您可能想知道为什么 T 是一个对象以及为什么这个“对象”甚至存在的地方?在这种情况下,T[] 正是我认为 Array<T> 的另一种语法,它与 Array 协变。由于类型实际上不同,我认为这两种情况相似。

Note that both a basic Object and a basic Array are not requirements for an OO language. C++ is the perfect example for this. The caveat of not having a basic type for these basic constructs is not being able to work with arrays or objects using reflection. For objects you're used to making Foo things which makes an 'object' feel natural. In reality, not having an array base class makes it equally impossible to do Foo -- which is not as frequently used, but equally important for the paradigm.

请注意,基本对象和基本数组都不是面向对象语言的要求。C++ 是一个完美的例子。没有这些基本构造的基本类型的警告是不能使用反射处理数组或对象。对于习惯于制作 Foo 东西的对象,这使“对象”感觉很自然。实际上,没有数组基类使得同样不可能执行 Foo —— 这不是经常使用的,但对范式同样重要。

Therefore, having C# without an Array base type, but with the riches of runtime types (particularly reflection) is IMO impossible.

因此,让 C# 没有 Array 基类型,但具有丰富的运行时类型(特别是反射)是 IMO 不可能的。

So more into the details...

所以更多细节......

Where are arrays used and why are they arrays

数组在哪里使用以及它们为什么是数组

Having a basic type for something as fundamental as an array is used for a lot of things and with good reason:

对于像数组这样基本的东西,有一个基本类型可以用于很多事情,并且有充分的理由:

  • Simple arrays
  • 简单数组

Yea well, we already knew that people use T[], just like they use List<T>. Both implement a common set of interfaces, to be exact: IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollectionand IEnumerable.

是的,我们已经知道人们使用T[],就像他们使用List<T>. 这两个实施一套通用的接口,确切的说:IList<T>ICollection<T>IEnumerable<T>IListICollectionIEnumerable

You can easily create an Array if you know this. We also all know this to be true, and it's not exciting, so we're moving on...

如果您知道这一点,您可以轻松创建一个数组。我们也都知道这是真的,这并不令人兴奋,所以我们继续......

  • Create collections.
  • 创建集合。

If you dig into List you will end up with an Array eventually - to be exact: a T[] array.

如果你深入研究 List 你最终会得到一个 Array - 准确地说:一个 T[] 数组。

So why's that? While you could have used a pointer structure (LinkedList), it's just not the same. Lists are continuous blocks of memory and get their speed by being a continuous block of memory. There's a lot of reasons about this, but simply put: processing continuous memory is the fastest way of processing memory - there are even instructions for that in your CPU that make it faster.

那是为什么?虽然您可以使用指针结构 (LinkedList),但它只是不一样。列表是连续的内存块,通过连续的内存块来提高速度。这有很多原因,但简单地说:处理连续内存是处理内存的最快方式 - 在你的 CPU 中甚至有指令可以使它更快。

A careful reader might point at the fact that you don't need an array for this, but a continuous block of elements of type 'T' that IL understands and can process. In other words, you could get rid of the Array type here, as long as you make sure there's another type that can be used by IL to do the same thing.

细心的读者可能会指出,为此您不需要数组,而是需要 IL 理解并可以处理的连续的“T”类型元素块。换句话说,您可以在此处去掉 Array 类型,只要您确保 IL 可以使用另一种类型来做同样的事情。

Note that there's value and class types. In order to retain the best possible performance, you need to store them in your block as-such... but for marshalling it's simply a requirement.

请注意,有值和类类型。为了保持最佳性能,您需要将它们原样存储在块中……但对于编组,这只是一个要求。

  • Marshalling.
  • 编组。

Marshalling uses basic types that all languages agree upon to communicate. These basic types are things like byte, int, float, pointer... and array. Most notably is the way arrays are used in C/C++, which is like this:

编组使用所有语言都同意进行通信的基本类型。这些基本类型是像字节、整数、浮点数、指针……和数组之类的东西。最值得注意的是数组在 C/C++ 中的使用方式,如下所示:

for (Foo *foo = beginArray; foo != endArray; ++foo) 
{
    // use *foo -> which is the element in the array of Foo
}

Basically this sets a pointer at the start of the array and increments the pointer (with sizeof(Foo) bytes) until it reaches the end of the array. The element is retrieved at *foo - which gets the element the pointer 'foo' is pointing at.

基本上这会在数组的开头设置一个指针并递增指针(使用 sizeof(Foo) 字节)直到它到达数组的末尾。在 *foo 处检索元素 - 获取指针 'foo' 指向的元素。

Note again that there are value types and reference types. You really don't want a MyArray that simply stores everything boxed as an object. Implementing MyArray just got a hell of a lot more tricky.

再次注意,有值类型和引用类型。您真的不希望 MyArray 只是将所有装箱的内容存储为对象。实现 MyArray 变得更加棘手。

Some careful readers can point at the fact here that you don't really need an array here, which is true. You need a continuous block of elements with the type Foo - and if it's a value type, it must be stored in the block as the (byte representation of the) value type.

一些细心的读者可以指出这里实际上并不需要数组的事实,这是真的。你需要一个连续的 Foo 类型的元素块——如果它是一个值类型,它必须作为值类型的(字节表示)存储在块中。

  • Multi-dimensional arrays
  • 多维数组

So more... What about multi-dimensionality? Apparently the rules aren't so black and white, because suddenly we don't have all the base classes anymore:

那么更多......多维呢?显然规则不是那么黑白分明,因为突然间我们不再拥有所有基类:

int[,] foo2 = new int[2, 3];
foreach (var type in foo2.GetType().GetInterfaces())
{
    Console.WriteLine("{0}", type.ToString());
}

Strong type just went out of the window, and you end up with collection types IList, ICollectionand IEnumerable. Hey, how are we supposed to get the size then? When using the Array base class, we could have used this:

强类型刚刚走出窗口,你最终得到了集合类型IList,ICollectionIEnumerable。嘿,那我们应该如何获得尺寸呢?当使用 Array 基类时,我们可以使用这个:

Array array = foo2;
Console.WriteLine("Length = {0},{1}", array.GetLength(0), array.GetLength(1));

... but if we look at the alternatives like IList, there's no equivalent. How are we going to solve this? Should introduce a IList<int, int>here? Surely this is wrong, because the basic type is just int. What about IMultiDimentionalList<int>? We can do that and fill it up with the methods that are currently in Array.

...但如果我们看看像 的替代方案IList,则没有等价物。我们将如何解决这个问题?IList<int, int>这里应该介绍一下吗?这肯定是错误的,因为基本类型只是int. 怎么样IMultiDimentionalList<int>?我们可以这样做并用当前在 Array 中的方法填充它。

  • Arrays have a fixed size
  • 数组有固定大小

Have you noticed that there are special calls for reallocating arrays? This has everything to do with memory management: arrays are so low-level, that they don't understand what growth or shrinking are. In C you would use 'malloc' and 'realloc' for this, and you really should implement your own 'malloc' and 'realloc' to understand why exactly having fixed sizes is important for allthings you directly allocate.

您是否注意到有重新分配数组的特殊调用?这与内存管理有关:数组是如此低级,以至于它们不了解增长或收缩是什么。在 C 中,您将为此使用 'malloc' 和 'realloc',并且您确实应该实现自己的 'malloc' 和 'realloc' 以了解为什么确切地具有固定大小对于您直接分配的所有事物都很重要。

If you look at it, there's only a couple of things that get allocated in a 'fixed' sizes: arrays, all basic value types, pointers and classes. Apparently we handle arrays differently, just like we handle basic types differently.

如果你看看它,只有几个东西以“固定”大小分配:数组、所有基本值类型、指针和类。显然,我们以不同的方式处理数组,就像我们以不同的方式处理基本类型一样。

A side note about type safety

关于类型安全的旁注

So why need these all these 'access point' interfaces in the first place?

那么为什么首先需要所有这些“接入点”接口呢?

The best practice in all cases is to provide users with a type safe point of access. This can illustrated by comparing code like this:

在所有情况下的最佳实践是为用户提供类型安全的访问点。这可以通过比较这样的代码来说明:

array.GetType().GetMethod("GetLength").Invoke(array, 0); // don't...

to code like this:

像这样编码:

((Array)someArray).GetLength(0); // do!

Type safety enable you to be sloppy when programming. If used correctly, the compiler will find the error if you made one, instead of finding it out run-time. I cannot stress enough how important this is - after all, your code might not be called in a test case at all, while the compiler will always evaluate it!

类型安全使您在编程时可以马虎。如果使用得当,编译器会发现错误,而不是在运行时发现错误。我怎么强调这有多重要 - 毕竟,您的代码可能根本不会在测试用例中被调用,而编译器将始终对其进行评估!

Putting it all together

把这一切放在一起

So... let's put it all together. We want:

所以……让我们把它们放在一起。我们想要:

  • A strongly typed block of data
  • That has its data stored continuously
  • IL support to make sure we can use the cool CPU instructions that make it bleeding fast
  • A common interface that exposes all the functionality
  • Type safety
  • Multi-dimensionality
  • We want value types to be stored as value types
  • And the same marshalling structure as any other language out there
  • And a fixed size because that makes memory allocation easier
  • 强类型数据块
  • 其数据连续存储
  • IL 支持以确保我们可以使用酷炫的 CPU 指令,使其快速流血
  • 公开所有功能的通用接口
  • 类型安全
  • 多维度
  • 我们希望将值类型存储为值类型
  • 以及与任何其他语言相同的编组结构
  • 和固定大小,因为这使内存分配更容易

That's quite a bit of low level requirements for any collection... it requires memory to be organized in a certain way as well as conversion to IL/CPU... I'd say there's a good reason it's considered a basic type.

对于任何集合来说,这都是相当低级的要求……它需要以某种方式组织内存以及转换为 IL/CPU……我想说它被认为是基本类型是有充分理由的。

回答by Alexei Levenkov

As everyone says - original Arrayis non-generic because there was no generics when it came into existence in v1. Speculation below...

正如每个人所说 - originalArray是非泛型的,因为它在 v1 中出现时没有泛型。以下猜测...

To make "Array" generic (which would make sense now) you can either

要使“数组”通用(现在有意义),您可以

  1. keep existing Arrayand add generic version. This is nice, but most usages of "Array" involve growing it over time and it most likely reason that better implementation of the same concept List<T>was implemented instead. At this point adding generic version of "sequential list of elements that does not grow" does not look very appealing.

  2. remove non-generic Arrayand replace with generic Array<T>implementation with the same interface. Now you have to make compiled code for older versions to work with new type instead of existing Arraytype. While it would be possible (also most likely hard) for framework code to support such migration, there is always a lot of code that written by other people.

    As Arrayis very basic type pretty much every piece of existing code (which includes custom code with reflection and marshalling to with native code and COM) uses it. As result price of even tiny incompatibility between versions (1.x -> 2.x of .Net Framework) would be very high.

  1. 保持现有Array并添加通用版本。这很好,但是“数组”的大多数用法都涉及随着时间的推移而增长,这很可能是因为实现了相同概念的更好实现List<T>。在这一点上,添加“不增长的元素顺序列表”的通用版本看起来不是很吸引人。

  2. 删除非泛型Array并替换为Array<T>具有相同接口的泛型实现。现在您必须为旧版本编译代码以使用新类型而不是现有Array类型。虽然框架代码有可能(也很可能很难)支持这种迁移,但总是有很多代码是由其他人编写的。

    作为Array非常基本的类型,几乎所有现有代码(包括具有反射和编组到本机代码和 COM 的自定义代码)都使用它。因此,即使版本(.Net Framework 的 1.x -> 2.x)之间的微小不兼容也会造成非常高的代价。

So as result Arraytype is there to stay forever. We now have List<T>as generic equivalent to be used.

因此,结果Array类型永远存在。我们现在List<T>可以使用通用的等价物。

回答by supercat

In addition to the other issues people have mentioned, trying to add a generic Array<T>would pose a few other difficulties:

除了人们提到的其他问题之外,尝试添加泛型Array<T>还会带来其他一些困难:

  • Even if today's covariance features had existed from the moment generics were introduced, they wouldn't have been sufficient for arrays. A routine which is designed to sort a Car[]will be able to sort a Buick[], even if it has to copy elements from the array into elements of type Carand then copy them back. The copying of the element from type Carback to a Buick[]isn't really type-safe, but it's useful. One could define a covariant array single-dimensional-array interface in such a way as to make sorting possible [e.g. by including a `Swap(int firstIndex, int secondIndex) method], but it would be difficult to make something that's as flexible as arrays are.

  • While an Array<T>type might work well for a T[], there would be no means within the generic type system to define a family that would include T[], T[,], T[,,], T[,,,], etc. for an arbitrary number of subscripts.

  • There is no means in .net to express the notion that two types should be considered identical, such that a variable of type T1can be copied to one of type T2, and vice versa, with both variables holding references to the same object. Someone using an Array<T>type would probably want to be able to pass instances to code which expects T[], and accept instances from code which uses T[]. If old-style arrays couldn't be passed to and from code that uses the new style, then the new-style arrays would be more of an obstacle than a feature.

  • 即使今天的协方差特性从引入泛型的那一刻起就已经存在,它们也不足以用于数组。一个旨在对 a 进行排序的例程Car[]将能够对 a 进行排序Buick[],即使它必须将数组中的元素Car复制到type 的元素中,然后再将它们复制回来。将元素从 type 复制Car回 aBuick[]并不是真正的类型安全,但它很有用。可以定义一个协变数组单维数组接口,使排序成为可能[例如通过包含一个`Swap(int firstIndex, int secondIndex) 方法],但是很难做出像这样灵活的东西数组是。

  • 虽然一个Array<T>类型可能为一个做工精良T[],将有通用型系统内没有办法确定一个家庭,将包括T[]T[,]T[,,]T[,,,],等为标的任意数量。

  • 在 .net 中没有办法表达两种类型应该被视为相同的概念,这样 type 的变量T1可以复制到 type 之一T2,反之亦然,两个变量都保存对同一对象的引用。使用Array<T>类型的人可能希望能够将实例传递给期望的代码T[],并从使用T[]. 如果旧式数组不能传入和传出使用新式的代码,那么新式数组将是一个障碍而不是功能。

There might be ways of jinxing the type system to allow for a type Array<T>that behaved as it should, but such a type would behave in many ways that were totally different from other generic types, and since there is already a type which implements the desired behavior (i.e. T[]), it's not clear what benefits would accrue from defining another.

可能有一些方法可以将类型系统混为一谈以允许一种Array<T>行为正常的类型,但是这种类型的行为方式与其他泛型类型完全不同,并且因为已经有一个类型实现了所需的行为(即T[]),尚不清楚定义另一个会带来什么好处。

回答by user1758003

Maybe I'm missing something but unless the array instance is casted to or used as an ICollection, IEnumerable, etc.. then you don't gain anything with an array of T.

也许我遗漏了一些东西,但除非数组实例被强制转换为或用作 ICollection、IEnumerable 等,否则你不会从 T 数组中获得任何东西。

Arrays are fast and are already type safe and don't incur any boxing/unboxing overhead.

数组速度很快,并且已经是类型安全的,并且不会产生任何装箱/拆箱开销。