当我们使用动态类型而不是对象类型时
天气我们可以克服装箱/拆箱开销吗?
void Print(dynamic p)
{
Console.WriteLine(string.Format("{0} : {1}", p.GetType(),p));
}
void Print(object p)
{
Console.WriteLine(string.Format("{0} : {1}", p.GetType(),p));
}
从这两种方法中,哪一种对处理器高效且友好?
在拳击方面,它不会有任何区别。采取以下代码:
public class TestClass
{
static void Test()
{
int v = 5; // Create an unboxed value type variable
PrintDynamic(v);
}
static void PrintDynamic(dynamic p)
{
}
}
如果我将方法反编译Test()
为 IL,我会得到:
.method private hidebysig static void Test() cil managed
{
// Code size 16 (0x10)
.maxstack 1
.locals init ([0] int32 v)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: box [mscorlib]System.Int32
IL_0009: call void ConsoleApplication6.Session::PrintDynamic(object)
IL_000e: nop
IL_000f: ret
}
您可以看到,尽管参数Test
被声明为dynamic
,但整数被装箱了。
编辑
在回答您关于时间差异的问题时,请考虑以下方法:
static void Print(object p)
{
string.Format("{0}", p);
}
static void PrintDynamic(dynamic p)
{
string.Format("{0}", p);
}
第一个 IL 看起来像这样:
.method private hidebysig static void Print(object p) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "{0}"
IL_0006: ldarg.0
IL_0007: call string [mscorlib]System.String::Format(string,
object)
IL_000c: pop
IL_000d: ret
} // end of method TestClass::Print
对于第二个:
.method private hidebysig static void PrintDynamic(object p) cil managed
{
.param [1]
.custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( 01 00 00 00 )
// Code size 123 (0x7b)
.maxstack 8
.locals init ([0] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000)
IL_0000: nop
IL_0001: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>> ConsoleApplication6.TestClass/'<PrintDynamic>o__SiteContainer0'::'<>p__Site1'
IL_0006: brtrue.s IL_0055
IL_0008: ldc.i4 0x100
IL_000d: ldstr "Format"
IL_0012: ldnull
IL_0013: ldtoken ConsoleApplication6.TestClass
IL_0018: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_001d: ldc.i4.3
IL_001e: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_0023: stloc.0
IL_0024: ldloc.0
IL_0025: ldc.i4.0
IL_0026: ldc.i4.s 33
IL_0028: ldnull
IL_0029: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
string)
IL_002e: stelem.ref
IL_002f: ldloc.0
IL_0030: ldc.i4.1
IL_0031: ldc.i4.3
IL_0032: ldnull
IL_0033: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
string)
IL_0038: stelem.ref
IL_0039: ldloc.0
IL_003a: ldc.i4.2
IL_003b: ldc.i4.0
IL_003c: ldnull
IL_003d: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
string)
IL_0042: stelem.ref
IL_0043: ldloc.0
IL_0044: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
string,
class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Type>,
class [mscorlib]System.Type,
class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_0049: call class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
IL_004e: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>> ConsoleApplication6.TestClass/'<PrintDynamic>o__SiteContainer0'::'<>p__Site1'
IL_0053: br.s IL_0055
IL_0055: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>> ConsoleApplication6.TestClass/'<PrintDynamic>o__SiteContainer0'::'<>p__Site1'
IL_005a: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>>::Target
IL_005f: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>> ConsoleApplication6.TestClass/'<PrintDynamic>o__SiteContainer0'::'<>p__Site1'
IL_0064: ldtoken [mscorlib]System.String
IL_0069: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_006e: ldstr "{0}"
IL_0073: ldarg.0
IL_0074: callvirt instance void class [mscorlib]System.Action`4<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object>::Invoke(!0,
!1,
!2,
!3)
IL_0079: nop
IL_007a: ret
} // end of method TestClass::PrintDynamic
这在某种程度上解释了为什么dynamic
最初使用需要更长的时间。我怀疑存在一些缓存机制,这意味着您在第一次之后不会受到性能影响。
可能根本没有区别。来自微软:
在大多数情况下,动态类型的行为类似于类型对象。但是,包含动态类型表达式的操作不会被编译器解析或类型检查。
http://msdn.microsoft.com/en-us/library/vstudio/dd264741.aspx
由此我推断,动态将作为一个对象,直到它被用于表达式中。这包括参数传递。
如果我没记错的话:
第一次使用动态类型时,编译器将生成一个调用站点,它将用于变量的所有后续读取,因此第一次命中很慢。
初始后每次读取所花费的额外时间是查找缓存的调用站点,然后执行拆箱。
不要犹豫,纠正我或在评论中添加这个答案!