gpt4 book ai didi

c# - 为什么 Array 不是泛型类型?

转载 作者:行者123 更新时间:2023-12-02 08:33:10 32 4
gpt4 key购买 nike

Array声明:

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

我想知道为什么不是:
public partial class Array<T>
: ICloneable, IList<T>, ICollection<T>, IEnumerable<T> {
  • 如果将其声明为泛型类型会出现什么问题?
  • 如果它是泛型类型,我们还需要非泛型类型还是可以从 Array<T> 派生出来? ?如
    public partial class Array: Array<object> { 
  • 最佳答案

    历史

    What problems would arise if arrays became a generic type?



    在 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[] ,你以前可以做的事情(即使它坏了)。因此,使数组通用会破坏很多代码。

    只有在 C# 4.0 中才引入了接口(interface)的协变/逆变泛型类型。这使得可以一劳永逸地修复损坏的数组协方差。但同样,这会破坏很多现有的代码。
    Array<Mammoth> mammoths = new Array<Mammoth>(10);
    Array<Animal> animals = mammoths; // Not allowed.
    IEnumerable<Animals> animals = mammoths; // Covariant conversion

    数组实现通用接口(interface)

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



    感谢每个数组的运行时技巧 T[]确实实现 IEnumerable<T> , ICollection<T>IList<T>自动。1 来自 Array class documentation :

    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.



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



    不。文档继续这样评论:

    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.



    那是因为(例如) ICollection<T>有一个 Add方法,但您不能向数组添加任何内容。它会抛出异常。这是 .NET Framework 中早期设计错误的另一个示例,它将在运行时向您抛出异常:
    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> interface这也由 hood1 下的数组实现,但它仅包含 Count所以用途有限。

    基类 Array

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



    在早期,我们做到了。所有数组都实现了非泛型 IList ,
    ICollection
    IEnumerable 接口(interface)通过它们的基类 Array .这是为所有数组提供特定方法和接口(interface)的唯一合理方式,并且是 Array 的主要用途。基类。您会看到枚举的相同选择:它们是值类型但从 Enum 继承成员;和从 MulticastDelegate 继承的委托(delegate).

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



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

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

    Could the Array base class derive from Array<object>?



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

    future

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



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

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

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

    Alternatives to array covariance?



    所有数组都实现 IList<T> .如果 IList<T>接口(interface)被制成适当的协变接口(interface)然后你可以转换任何数组 Array<Mammoth> (或任何与此相关的列表)到 IList<Animal> .但是,这需要 IList<T>要重写的接口(interface)以删除所有可能更改底层数组的方法:
    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 对于 ContainsIndexOf 已经足够了,当传递一个对象时,它们只会返回 false类型不正确。实现这些接口(interface)的集合可以提供自己的通用 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>上面描述的接口(interface),它工作得很好。我还添加了不变式 IMutableList<T>IMutableCollection<T>提供我的新 IList<T> 的变异方法的接口(interface)和 ICollection<T>接口(interface)缺乏。

    我围绕它建立了一个简单的收藏库,你可以 download the source code and compiled binaries from BitBucket ,或安装 NuGet 包:

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



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

    关于c# - 为什么 Array 不是泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14324987/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com