在C#3.0中有一个动态类型,var,它可以定义不同的类型,系统在第一次编译时会根据上下文来判断这个变量的准确类型。
下面看个例子:
C#代码:
static void Main(string[] args)
{
var i = 10;
var d = 1.2;
var f = 1.1f;
var str = new string[] { "a","b"};
}
IL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 50 (0x32)
.maxstack 3
.locals init ([0] int32 i,
[1] float64 d,
[2] float32 f,
[3] string[] str,
[4] string[] CS$0$0000)
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0
IL_0004: ldc.r8 1.2
IL_000d: stloc.1
IL_000e: ldc.r4 1.1
IL_0013: stloc.2
IL_0014: ldc.i4.2
IL_0015: newarr [mscorlib]System.String
IL_001a: stloc.s CS$0$0000
IL_001c: ldloc.s CS$0$0000
IL_001e: ldc.i4.0
IL_001f: ldstr "a"
IL_0024: stelem.ref
IL_0025: ldloc.s CS$0$0000
IL_0027: ldc.i4.1
IL_0028: ldstr "b"
IL_002d: stelem.ref
IL_002e: ldloc.s CS$0$0000
IL_0030: stloc.3
IL_0031: ret
} // end of method Program::Main
我们可以看到,在C#中定义了几种类型,都是以var定义的,并且都是方法内部的局部变量。但在IL中我们会看到(红色代码)定义的局部变量会有准确的类型。
下面我们来看一下在vs2010中,C#4.0中的一个新的类型dynamic,我们同样来定义一个方法:
static void Main()
{
var i = 10;
var d = 1.2;
var f = 1.1f;
var str = new string[] { "a", "b" };
dynamic i1 = 10;
dynamic d1 = 1.2;
dynamic f1 = 1.1f;
dynamic str1 = new string[] { "a", "b" };
}
再来看看他的IL
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 117 (0x75)
.maxstack 3
.locals init ([0] int32 i,
[1] float64 d,
[2] float32 f,
[3] string[] str,
[4] object i1,
[5] object d1,
[6] object f1,
[7] object str1,
[8] string[] CS$0$0000)
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0
IL_0004: ldc.r8 1.2
IL_000d: stloc.1
IL_000e: ldc.r4 1.1
IL_0013: stloc.2
IL_0014: ldc.i4.2
IL_0015: newarr [mscorlib]System.String
IL_001a: stloc.s CS$0$0000
IL_001c: ldloc.s CS$0$0000
IL_001e: ldc.i4.0
IL_001f: ldstr "a"
IL_0024: stelem.ref
IL_0025: ldloc.s CS$0$0000
IL_0027: ldc.i4.1
IL_0028: ldstr "b"
IL_002d: stelem.ref
IL_002e: ldloc.s CS$0$0000
IL_0030: stloc.3
IL_0031: ldc.i4.s 10
IL_0033: box [mscorlib]System.Int32
IL_0038: stloc.s i1
IL_003a: ldc.r8 1.2
IL_0043: box [mscorlib]System.Double
IL_0048: stloc.s d1
IL_004a: ldc.r4 1.1
IL_004f: box [mscorlib]System.Single
IL_0054: stloc.s f1
IL_0056: ldc.i4.2
IL_0057: newarr [mscorlib]System.String
IL_005c: stloc.s CS$0$0000
IL_005e: ldloc.s CS$0$0000
IL_0060: ldc.i4.0
IL_0061: ldstr "a"
IL_0066: stelem.ref
IL_0067: ldloc.s CS$0$0000
IL_0069: ldc.i4.1
IL_006a: ldstr "b"
IL_006f: stelem.ref
IL_0070: ldloc.s CS$0$0000
IL_0072: stloc.s str1
IL_0074: ret
} // end of method File::Main
我们看到C#中的代码基本没变,就是把var换成dynamic,在IL中,var的没有变化,但用dynamic定义的类型(红色代码部分),除了string[]外都是object类型,如果这样看来,dynamic其不是就是object吗?不是的,往下看,会发现每个类型在初始化时就个box,就是装箱,装到dynamic这种类型中了,看来dynamic是个引用类型,是真的吗?
现在我们从装折箱角度来看一下这dynamic与object的区别。
static void Method()
{
int i = 10;
object o = i;
int j = (int)o;
dynamic d = i;
int k = (int)d;
}
来看一下IL:
.method private hidebysig static void Method() cil managed
{
// Code size 89 (0x59)
.maxstack 4
.locals init ([0] int32 i,
[1] object o,
[2] int32 j,
[3] object d,
[4] int32 k)
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: box [mscorlib]System.Int32
IL_000a: stloc.1
IL_000b: ldloc.1
IL_000c: unbox.any [mscorlib]System.Int32
IL_0011: stloc.2
IL_0012: ldloc.0
IL_0013: box [mscorlib]System.Int32
IL_0018: stloc.3
IL_0019: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>> ConsoleApplication2.File/'<Method>o__SiteContainer0'::'<>p__Site1'
IL_001e: brtrue.s IL_0041
IL_0020: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance()
IL_0025: ldtoken [mscorlib]System.Int32
IL_002a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_002f: ldc.i4.1
IL_0030: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpConversionPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder,
class [mscorlib]System.Type,
valuetype [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpConversionPayload/ConversionKindEnum)
IL_0035: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder)
IL_003a: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>> ConsoleApplication2.File/'<Method>o__SiteContainer0'::'<>p__Site1'
IL_003f: br.s IL_0041
IL_0041: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>> ConsoleApplication2.File/'<Method>o__SiteContainer0'::'<>p__Site1'
IL_0046: ldfld !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>>::Target
IL_004b: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>> ConsoleApplication2.File/'<Method>o__SiteContainer0'::'<>p__Site1'
IL_0050: ldloc.3
IL_0051: callvirt instance !2 class [System.Core]System.Func`3<class [System.Core]System.Scripting.Actions.CallSite,object,int32>::Invoke(!0,
!1)
IL_0056: stloc.s k
IL_0058: ret
} // end of method File::Method
我们会发现int i到object o是box,object o到int i是unbox,但dynamic d = i; int k = (int)d;前一行代码是box,但后一行就不是简单的unbox,看来object与dynamic则是不同的,是一个全新的类型,当然低层做了很多工作。
var与dynamic还一个区别是应用范围,var只能在类成员内部去应用,也就是来充当类成员的局部变量,但dynamic的应用范围就大了,他和一个基本的类型是一样的,可以在有其他类型的任何地方应用。也就是我们的变量动态到任何地方了,不像var只是在一定范围内。
dynamic 虽然简化了我们的定义,但这是以牺牲系统性能为代价的。所以大家最好能有准确的数据类型。