C#中数组的神奇之处是什么?

int[] a = new int[5]; string[] b = new string[1]; 

ab的类型都inheritance自抽象的System.Array ,但是内置库中没有真正的类(似乎有一些运行时类型,你找不到int[]的类型defination类int[] )。 你能告诉我编译时会发生什么吗? 为什么他们(c#团队)制作这个设计(我的意思是为什么它不像Array ,而是他们使用带编译器魔法的抽象类)?

试图在.NET类型系统中解释这一点并不会让你走得太远。 JIT编译器和CLR内置了核心支持来处理创建数组。 这样的陈述:

  var arr = new int[5]; 

生成此IL:

  IL_0001: ldc.i4.5 IL_0002: newarr [mscorlib]System.Int32 

然后JIT编译器将其转换为此机器代码:

 00000035 mov edx,5 ; arg2 = array size 0000003a mov ecx,6F535F06h ; arg1 = typeof(int) 0000003f call FFD52128 ; call JIT_NewArr1(type, size) 

这里的核心成分是专用的IL操作码newarr ,而不是通常的newobj操作码,它创建了一个类的实例。 并简单地转换为实际获取对象的CLR帮助函数。 您可以使用SSCLI20源代码clr\src\vm\jithelpers.cpp查看此辅助函数。 这里发布的内容太大了,但它经过大量优化,可以尽可能快地运行这种代码,可以直接访问CLR代码可用的类型内部。

有两个这样的助手可用,JIT_NewArr1()创建一维(向量)数组,JIT_NewMDArr()创建多维数组。 与Type.MakeArrayType()可用的两个重载进行比较。

为什么他们(c#团队)制作这个设计(我的意思是为什么它不像Array那样……

generics是定义容器的理想选择,因为它们约束元素类型,因此您无法插入类型A并尝试检索类型B.

但是直到CLR2 / C#2才添加generics。 因此arrays必须以自己的方式提供类型安全。

即便如此,它与generics并没有什么不同。 您注意到int[]没有特殊的类。 但也不会有Array 。 在generics中,只有generics类Array ,而CLR“神奇地”为您使用的不同类型参数创建专用版本。 因此,如果使用generics,那将毫不逊色。

尽管如此,在CLR中,任何对象的类型都是有效的(它可以作为一个值来操作),类型为Type ,并且可以使用typeof获得。 因此,虽然没有任何数组类型的代码声明(为什么你需要看到它?),你可以查询一个Type对象。

顺便说一下,数组约束元素类型的方式存在设计缺陷。 你可以声明一个数组:

 int[] ints = ... 

然后,您可以将其存储在更宽松的变量中:

 object[] objs = ints; 

但这意味着您可以插入一个字符串(至少它在编译时出现):

 objs[3] = "Oh dear"; 

在运行时它会抛出exception。 静态类型检查的想法是在编译时捕获这种东西,而不是运行时。 generics不会出现此问题,因为它们不会根据类型参数的兼容性为generics类实例赋予赋值兼容性。 (自从C#4 / CLR4以来,他们已经获得了在有意义的地方做到这一点的能力,但这对于可变数组来说是没有意义的。)

查看Array类。

使用[]语法声明数组时,编译器会在后台使用此类。

对于C#, []成为inheritance自System.Array的类型。

来自C#4.0规范:

§12.1.1System.Array类型

System.Array类型是所有数组类型的抽象基类型。 从任何数组类型到System.Array都存在隐式引用转换(第6.1.6节),并且从System.Array到任何数组类型都存在显式引用转换(第6.2.4节)。 请注意,System.Array本身不是数组类型。 相反,它是一个类类型,从中派生所有数组类型。

有这样的课。 您不能inheritance它,但是当您编写“int []”时,编译器会创建一个inheritanceSystem.Array的类型。 所以如果你声明一个变量:

 int[] x; 

此变量将具有inheritanceSystem.Array的类型,因此具有其所有方法和属性。

这也与代表类似。 定义委托时:

 delegate void Foo(int x); delegate int Bar(double x); 

然后类型Foo实际上是一个inheritanceSystem.MulticastDelegate的类,而Bar是一个inheritanceSystem.Delegate的类。

如果你想了解低级细节,我建议你获取ECMA 335规范并寻找arrays: http : //www.ecma-international.org/publications/standards/Ecma-335.htm

我开始研究ECMA 335规范 ,所以我想我会分享我读到的内容。

VES在需要时自动创建精确数组类型。 因此,对arrays类型的操作由CTS定义。 这些通常是:基于大小和下限信息分配数组,索引数组以读取和写入值,计算数组元素的地址(托管指针),以及查询排名,边界和存储在数组中的值的总数。

VES为每个可区分的数组类型创建一个数组类型。

向量是System.Array的子​​类型,是由CLI预定义的抽象类。 它提供了几种可应用于所有向量的方法。 见分区IV。

虽然向量(§II.14.1)通过CIL指令直接支持,但VES通过创建抽象类System.Array的子​​类型来支持所有其他数组(请参阅分区IV)

虽然向量(§II.14.1)通过CIL指令直接支持,但VES通过创建抽象类System.Array的子​​类型来支持所有其他数组(请参阅分区IV)

VES为数组创建的类包含几个方法,其实现由VES提供:

它继续陈述,非常详细,提供的方法是:

  1. 两个构造函数
  2. 得到
  3. 地址(返回托管指针)

VES表示虚拟执行系统 , CLR是它的实现 。

规范还详细说明了如何存储数组的数据(以行 – 主顺序连续),数组中允​​许的索引(仅基于0),创建向量时(单维,基于0的数组),而不是当使用CIL指令newarr而不是newobj (创建基于0的单维数组)时,使用不同的数组类型。

基本上编译器必须做的一切都是为了构建方法查找表等…对于常规类型,它必须为数组做,但它们只是在编译器/ JIT中编写了一个更通用和稍微特殊的行为。

他们为什么这么做? 可能是因为数组是特殊的,广泛使用的,并且可以以优化的方式存储。 C#团队并不一定做出这个决定。 它更像是.NET的东西,它是Mono和Portable.NET的堂兄,所有这些都是CIL的东西。

数组是CLR特有的。 它们被分配了’newarr’指令,并且使用’ldelem *’和’stelem *’指令访问元素,而不是通过System.Array方法;

请参阅http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newarr.aspx

您可以查看ildasm输出以查看数组的表示方式。

所以,回答你的问题 – 没有为任何特定的数组生成新的类型声明。

[]是用于在c#中定义数组的语法(合成糖)。 也许CreateInstance将在运行时被替换

  Array a = Array.CreateInstance(typeof(int), 5); 

和…一样

 int[] a = new int[5]; 

CreateInstance的来源(取自reflection器)

 public static unsafe Array CreateInstance(Type elementType, int length) { if (elementType == null) { throw new ArgumentNullException("elementType"); } RuntimeType underlyingSystemType = elementType.UnderlyingSystemType as RuntimeType; if (underlyingSystemType == null) { throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "elementType"); } if (length < 0) { throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } return InternalCreate((void*) underlyingSystemType.TypeHandle.Value, 1, &length, null); }