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[]
时,不要担心差异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属性,它只有两个方法Get和Set——这些方法在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.