C# 为什么';数组类是否直接公开其索引器?

C# 为什么';数组类是否直接公开其索引器?,c#,arrays,ilist,indexer,C#,Arrays,Ilist,Indexer,回答时要提及的内容: 当所讨论的项目是Array而不是t[]时,不要担心差异 多维数组的类似情况是[] 也就是说,N-dims到线性变换始终是可能的。所以这个问题特别引起了我的注意,因为它已经为线性索引器实现了IList 问题: 在我的代码中,我有以下声明: public static Array ToArray<T>(this T source); 但是我认为名为ToArray的方法返回的IList看起来很奇怪 因此,我对此感到困惑: 在数组的声明中,有 object ILi

回答时要提及的内容:

  • 当所讨论的项目是
    Array
    而不是
    t[]
    时,不要担心差异

  • 多维数组的类似情况是[]

  • 也就是说,N-dims到线性变换始终是可能的。所以这个问题特别引起了我的注意,因为它已经为线性索引器实现了
    IList


    问题:

    在我的代码中,我有以下声明:

    public static Array ToArray<T>(this T source); 
    
    但是我认为名为
    ToArray
    的方法返回的
    IList
    看起来很奇怪

    因此,我对此感到困惑:

    数组
    的声明中,有

    object IList.this[int index];
    
    这样我们就可以

    Array a;
    a=Array.CreateInstance(typeof(char), 1);
    (a as IList)[0]='a';
    
    但我们不能

    a[0]='a';
    
    除非它被宣布为

    public object this[int index]; 
    

    我能看到的唯一区别是,它要求我们通过实现它的接口
    IList
    显式地使用它的索引器,但是为什么呢有好处吗?还是存在暴露问题?

    a因为IList(基本上)正在铸造。因此,请先投下它:

    char[] a = (char[])Array.CreateInstance(typeof(char), 1);
    a[0] = 'a';
    

    编辑:原因是:
    数组
    的接口没有定义索引器。它使用
    SetValue(Object,Int32)
    objectgetvalue(Int32)
    。注意那里不祥的
    对象
    东西<代码>数组
    不是特定于类型的;它是为最低公分母而构建的:
    对象
    。它可以很容易地定义一个索引器,但在实践中,仍然会有un/装箱问题。

    我认为
    Array
    没有直接实现该索引器的一个原因是因为所有特定的数组类型(如
    char[]
    )都源自
    Array

    这意味着这样的代码是合法的:

    char[] array = new char[10];
    array[0] = new object();
    
    这样的代码不应该是合法的,因为它不是类型安全的。以下内容是合法的,并引发异常:

    char[] array = new char[10];
    array.SetValue(new object(), 0);
    

    但是通常不使用
    SetValue()
    ,因此这不是一个大问题。

    Array
    不能有索引器,因为它需要能够表示具有任意维数的数组。二维数组的索引器与一维数组的索引器具有不同的签名

    如果在表示二维数组的
    数组上提供并使用索引器,会发生什么情况

    语言设计者选择的解决方案是根本不包括索引器

    如果你知道你的<代码> ToArray < /C>方法总是返回一维数组,那么考虑使用:

    public static T[] ToArray<T>(this T source); 
    

    简短回答

    System.Array是N-D数组的基类(不仅仅是1-D),这就是为什么1-D索引器(对象this[i]{get;set;})不能是基成员的原因

    长答案

    如果您创建二维数组并尝试访问它的IList indexer:

    Array a;
    a=Array.CreateInstance(typeof(char), 1,1);
    (a as IList)[0]='a';
    
    您将得到不受支持的异常

    好问题是:

    为什么
    System.Array
    实现
    IList
    IEnumerable
    ,而对于非一维数组,它的大多数实现将抛出
    NotSupportedException


    还有一件有趣的事要提。从技术上讲,大多数数组内部都有经典意义上的类索引器。索引器的经典含义是属性“Item”+get(+set)方法。如果深入思考,您将看到typeof(string[])没有indexer属性,它只有两个方法GetSet——这些方法在string[]类中声明(不在基类中,与Array.SetValue、Array.GetValue不同),它们用于编译时索引

    数组
    中的
    IList
    方法(包括其索引器)的问题在于,它们的显式实现是:

    从.NET Framework 2.0开始,
    Array
    类实现了
    System.Collections.Generic.IList
    System.Collections.Generic.ICollection
    System.Collections.Generic.IEnumerable
    通用接口。这些实现在运行时提供给阵列,因此对于文档构建工具不可见。因此,通用接口不会出现在
    数组
    类的声明语法中,并且没有接口成员的参考主题,这些主题只能通过将数组强制转换为通用接口类型(显式接口实现)来访问

    当类显式实现接口时,访问接口方法:

    实现接口的类可以显式实现该接口的成员。当显式实现成员时,不能通过类实例访问它,只能通过接口的实例访问它

    提供“常规”(而不是“显式”)接口实现的问题在于
    Array
    类不是泛型的:如果没有类型参数,就无法编写

    class Array : IList<T>
    
    同时,
    a
    b
    c
    的静态类型保持不变-它是
    System.Array
    。但是,在运行时,
    a
    将实现
    IList
    b
    将实现
    IList
    c
    -
    IList
    。在编译时,这一切都是未知的,这会阻止编译器“看到”索引器和IList的其他方法

    此外,并非所有
    Array
    实例都实现
    IList
    -:


    以上所有这些都会阻止编译器在没有显式强制转换的情况下访问
    IList
    方法,包括索引器。

    即使如果数组都是1D,您仍然会遇到协方差和逆变问题:

    如果基类有

    public Object this[int index] { get; set; }
    
    属性索引器
    class Array : IList<T>
    
    // The type of [T] is char
    Array a = Array.CreateInstance(typeof(char), 1);
    // The type of [T] is int
    Array b = Array.CreateInstance(typeof(int), 1);
    // The type of [T] is string
    Array c = Array.CreateInstance(typeof(string), 1);
    
    Array x = new int[5];
    Console.WriteLine(x.GetType().GetInterface("IList`1") != null); // True
    Array y = new int[5,5];
    Console.WriteLine(y.GetType().GetInterface("IList`1") != null); // False
    
    public Object this[int index] { get; set; }
    
    public TValue this[int index] { get; set; }
    
    Add(Object value)
    
    Add(TValue value)
    
    static T GetFirst<T>(T[] a)
    {
       return a[0]:
    }
    
    L_0000: ldarg.0 
    L_0001: ldc.i4.0 
    L_0002: ldelem.any !!T
    L_0007: ret 
    
    private static T GetFirst<T>(IList<T> a)
    {
        return a[0];
    }
    
    L_0000: ldarg.0 
    L_0001: ldc.i4.0 
    L_0002: callvirt instance !0 [mscorlib]System.Collections.Generic.IList`1<!!T>::get_Item(int32)
    L_0007: ret 
    
    T Get(Int32)
    void Set(Int32, T)
    
    public static T[] ToArray<T>(this T source)
    
    IList myArray = ToArray(myValues);
    // myArray is actually a string array.
    myArray[0]='a';
    // blows up here as you can't explicitly cast a char to a string.