C#中数组的神奇之处是什么?
int[] a = new int[5]; string[] b = new string[1];
a
和b
的类型都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提供:
它继续陈述,非常详细,提供的方法是:
- 两个构造函数
- 得到
- 组
- 地址(返回托管指针)
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); }