int[] a = new int[5];
string[] b = new string[1];
a
和b
的类型都继承自抽象System.Array
,但内置库中没有真正的类(似乎有一些运行时类型,你找不到int[]
的类型定义类。你能告诉我编译时会发生什么吗?为什么他们(c#团队)制作这个设计(我的意思是为什么它不像Array<T>
,而是他们使用带编译器魔法的抽象类)?
答案 0 :(得分:18)
试图在.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()可用的两个重载。
答案 1 :(得分:9)
他们为什么(c#团队)制作 这个设计(我的意思是为什么它不是 像Array这样的东西......
泛型是定义容器的理想选择,因为它们约束了元素类型,因此您无法插入类型A并尝试检索类型B.
但是直到CLR2 / C#2才添加泛型。因此,阵列必须以自己的方式提供类型安全。
即便如此,它与泛型并没有什么不同。您注意到int[]
没有特殊课程。但也不会有Array<int>
。在泛型中,只有泛型类Array<T>
,而CLR“神奇地”为您使用的不同类型参数创建专用版本。因此,如果使用仿制药,那将毫不逊色。
尽管如此,在CLR中,任何对象的类型都会被定义(它存在为您可以操作的值),类型为Type
,并且可以使用typeof
获得。所以虽然没有任何数组类型的代码声明(为什么你需要看到它?),你可以查询一个Type
对象。
顺便说一句,数组约束元素类型的方式存在设计缺陷。您可以声明一个数组:
int[] ints = ...
然后,您可以将其存储在更宽松的变量中:
object[] objs = ints;
但这意味着你可以插入一个字符串(至少它在编译时出现):
objs[3] = "Oh dear";
在运行时它会抛出异常。静态类型检查的想法是在编译时捕获这种东西,而不是运行时。泛型不会遇到此问题,因为它们不会根据类型参数的兼容性为泛型类实例赋予赋值兼容性。 (自从C#4 / CLR4以来,他们已经获得了在有意义的地方做到这一点的能力,但这对于可变数组来说是没有意义的。)
答案 2 :(得分:7)
查看Array
课程。
使用[]
语法声明数组时,编译器会在幕后使用此类。
对于C#,[]
成为继承自System.Array
。
来自C#4.0规范:
§12.1.1System.Array类型
System.Array类型是所有数组类型的抽象基类型。从任何数组类型到System.Array都存在隐式引用转换(第6.1.6节),并且从System.Array到任何数组类型都存在显式引用转换(第6.2.4节)。请注意,System.Array本身不是数组类型。相反,它是一个类类型,从中派生所有数组类型。
答案 3 :(得分:2)
有这样的课。您不能继承它,但是当您编写“int []”时,编译器会创建一个继承System.Array的类型。因此,如果您声明一个变量:
int[] x;
此变量将具有继承System.Array的类型,因此具有其所有方法和属性。
这也与代表类似。定义委托时:
delegate void Foo(int x);
delegate int Bar(double x);
然后类型Foo
实际上是一个继承System.MulticastDelegate
的类,而Bar
是一个继承System.Delegate
的类。
答案 4 :(得分:2)
如果您想了解低级细节,我建议您获取ECMA 335规范并查找阵列:http://www.ecma-international.org/publications/standards/Ecma-335.htm
答案 5 :(得分:1)
我开始挖掘ECMA 335 spec,所以我想我会分享我所读的内容。
确切的数组类型是VES在需要时自动创建的。因此, 数组类型的操作由CTS定义。这些通常是:分配数组 基于大小和下限信息,索引数组以读取和写入值, 计算数组元素的地址(托管指针),并查询排名, bounds,以及存储在数组中的值的总数。
VES为每个创建一个数组类型 可区分的阵列类型。
向量是System.Array的子类型,是由CLI预定义的抽象类。它提供了几个 可以应用于所有向量的方法。见分区IV。
虽然向量(§II.14.1)通过CIL指令直接支持,但所有其他数组都受支持 VES通过创建抽象类System.Array的子类型(参见Partition IV)
虽然向量(§II.14.1)通过CIL指令直接支持,但所有其他数组都受支持 VES通过创建抽象类System.Array的子类型(参见Partition IV)
VES为数组创建的类包含几个提供实现的方法 由VES:
它继续陈述,提供的方法非常详细:
VES表示虚拟执行系统和the CLR is an implementation of it。
规范还详细说明了如何存储数组的数据(以行 - 主顺序连续),数组中允许的索引(仅基于0),创建向量时(单维,基于0的数组) )而不是使用不同的数组类型,当使用CIL指令newarr
而不是newobj
时(创建一个基于0的单维数组)。
基本上编译器必须做的一切都是为了构建方法查找表等。对于常规类型,它必须为数组做,但它们只是在编译器/ JIT中编写了一个更通用和稍微特殊的行为。
他们为什么这样做?可能是因为数组是特殊的,广泛使用的,并且可以以优化的方式存储。 C#团队并不一定做出这个决定。它更像是.NET的东西,它是Mono和Portable.NET的堂兄,所有这些都是CIL的东西。
答案 6 :(得分:0)
阵列对CLR来说很特殊。它们被分配了'newarr'指令,并且使用'ldelem *'和'stelem *'指令访问元素,而不是通过System.Array方法;
请参阅http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newarr.aspx
您可以查看ildasm输出以查看数组的表示方式。
所以,回答你的问题 - 没有为任何特定数组生成新的类型声明。
答案 7 :(得分:0)
[]是用于在c#中定义数组的语法(合成糖)。也许CreateInstance将在运行时替换
Array a = Array.CreateInstance(typeof(int), 5);
与
相同int[] a = new int[5];
CreateInstance的来源(取自反射器)
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);
}