c# – CIL – 拳击拆箱与Nullable

c# – CIL – 拳击拆箱与Nullable,第1张

概述如果我理解CLR如何处理事情和处理无效的方式,如 Boxing / Unboxing Nullable Types – Why this implementation?所述,仍然有些让我困惑的事情.例如,以下C#7代码 void C<T>(object o) where T : struct { if (o is T t) Console.WriteLine($"Argum 如果我理解CLR如何处理事情和处理无效的方式,如 Boxing / Unboxing Nullable Types – Why this implementation?所述,仍然有些让我困惑的事情.例如,以下C#7代码

voID C<T>(object o) where T : struct {    if (o is T t)        Console.Writeline($"Argument is {typeof(T)}: {t}");}

编译成以下CIL

IL_0000: ldarg.0IL_0001: isinst valuetype [mscorlib]System.Nullable`1<!!T>IL_0006: unBox.any valuetype [mscorlib]System.Nullable`1<!!T>IL_000b: stloc.1IL_000c: ldloca.s 1IL_000e: call instance !0 valuetype [mscorlib]System.Nullable`1<!!T>::GetValueOrDefault()IL_0013: stloc.0IL_0014: ldloca.s 1IL_0016: call instance bool valuetype [mscorlib]System.Nullable`1<!!T>::get_HasValue()IL_001b: brfalse.s IL_003cIL_001d: ldstr "Argument is {0}: {1}"IL_0022: ldtoken !!TIL_0027: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)IL_002c: ldloc.0IL_002d: Box !!TIL_0032: call string [mscorlib]System.String::Format(string,object,object)IL_0037: call voID [mscorlib]System.Console::Writeline(string)IL_003c: ret

但是下面的C#

voID D<T>(object o) where T : struct {    if (o is T)        Console.Writeline($"Argument is {typeof(T)}: {(T) o}");}

编译成以下CIL

IL_0000: ldarg.0IL_0001: isinst !!TIL_0006: brfalse.s IL_002cIL_0008: ldstr "Argument is {0}: {1}"IL_000d: ldtoken !!TIL_0012: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)IL_0017: ldarg.0IL_0018: unBox.any !!TIL_001d: Box !!TIL_0022: call string [mscorlib]System.String::Format(string,object)IL_0027: call voID [mscorlib]System.Console::Writeline(string)IL_002c: ret

我认为发生了什么:查看第一种方法的CIL,似乎(1)检查参数是否为[盒装?] Nullable< T>,如果是,则将其推入堆栈,否则为null,( 2)取消它(如果它为空?),(3)尝试获取其值,否则默认(T),(4)然后检查它是否有值,如果没有则分支出来.第二种方法的CIL足够简单,它只是试图取消打包参数.

如果两个代码的语义是等价的,为什么前一种情况涉及拆箱到Nullable< T>.而前一种情况“只是拆箱”?其次,在第一个CIL中,如果对象参数是一个盒装的int,我现在认为它正是它在tin上所说的(即盒装的int而不是盒装的Nullable< int>),不会isinst指令总是失败? Nullable< T>即使在CIL级别也能得到特殊待遇?

更新:在手写一些MSIL之后,看起来该对象(如果它确实是一个盒装的int)可以解包到int或Nullable< int>中.

.method private static voID Foo(object o) cil managed {    .maxstack 1    ldarg.0    isinst int32    brfalse.s L_00    ldarg.0    unBox.any int32    call voID [mscorlib]System.Console::Writeline(int32)L_00:    ldarg.0    isinst valuetype [mscorlib]System.Nullable`1<int32>    brfalse.s L_01    ldarg.0    unBox valuetype [mscorlib]System.Nullable`1<int32>    call instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()    call voID [mscorlib]System.Console::Writeline(int32)L_01:    ldarg.0    unBox valuetype [mscorlib]System.Nullable`1<int32>    call instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()    brtrue.s L_02    ldstr "No value!"    call voID [mscorlib]System.Console::Writeline(string)L_02:    ret}
解决方法 C#7中的新语法是一次进行类型检查和类型转换.在旧版本中,这通常以两种可能的方式完成.

if(o is T)    //use (T)o
T t = o as T;if(t != null)    //use t

对于引用类型,第一个具有冗余转换,因为is被编译为isinst和条件分支,正如您可以从使用的CIL指令中看到的那样.第二个代码与CIL方面的第一个代码相同,减去额外的(T)o cast(编译为castclass).

对于值类型,第二个选项只能以可空类型完成,我也认为它实际上比第一个慢(必须创建一个结构).

我已经将以下方法编译为CIL:

static voID C<T>(object o) where T : struct{    T? t = o as T?;    if(t != null)        Console.Writeline("Argument is {0}: {1}",typeof(T),t);}

生成此代码:

.method private hIDebysig static voID  C<valuetype .ctor ([mscorlib]System.ValueType) T>(object o) cil managed{  // Code size       48 (0x30)  .maxstack  3  .locals init (valuetype [mscorlib]System.Nullable`1<!!T> V_0)  IL_0000:  ldarg.0  IL_0001:  isinst     valuetype [mscorlib]System.Nullable`1<!!T>  IL_0006:  unBox.any  valuetype [mscorlib]System.Nullable`1<!!T>  IL_000b:  stloc.0  IL_000c:  ldloca.s   V_0  IL_000e:  call       instance bool valuetype [mscorlib]System.Nullable`1<!!T>::get_HasValue()  IL_0013:  brfalse.s  IL_002f  IL_0015:  ldstr      "Argument is {0}: {1}"  IL_001a:  ldtoken    !!T  IL_001f:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)  IL_0024:  ldloc.0  IL_0025:  Box        valuetype [mscorlib]System.Nullable`1<!!T>  IL_002a:  call       voID [mscorlib]System.Console::Writeline(string,object)  IL_002f:  ret}

除了调用GetValueOrDefault之外,这正是问题中的代码,因为我没有获得可空实例的实际值.

可以为空的类型不能直接装箱或取消装箱,只能通过它们的底层值,或者作为普通的空值.第一个isinst确保其他类型不会产生异常(我想isinst !! T也可以使用),而只是一个空引用.然后,unBox.any *** 作码从引用中形成一个可为空的实例,然后像往常一样使用它.该指令也可以写为空检查并自行构成可空实例,但这种方式更短.

C#7使用第二种方式为T t,因此如果T是值类型,则除了使用可空类型之外别无选择.为什么不选择以前的选项呢?我只能猜测它在语义或实现,变量分配等方面可能会有一些实质性的差异.因此,他们选择与新构造的实现保持一致.

为了比较,这是我在上面的方法中改变T:struct到T:class时生成的内容(以及T?到T):

.method private hIDebysig static voID  C<class T>(object o) cil managed{  // Code size       47 (0x2f)  .maxstack  3  .locals init (!!T V_0)  IL_0000:  ldarg.0  IL_0001:  isinst     !!T  IL_0006:  unBox.any  !!T  IL_000b:  stloc.0  IL_000c:  ldloc.0  IL_000d:  Box        !!T  IL_0012:  brfalse.s  IL_002e  IL_0014:  ldstr      "Argument is {0}: {1}"  IL_0019:  ldtoken    !!T  IL_001e:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)  IL_0023:  ldloc.0  IL_0024:  Box        !!T  IL_0029:  call       voID [mscorlib]System.Console::Writeline(string,object)  IL_002e:  ret}

再次与原始方法相当一致.

总结

以上是内存溢出为你收集整理的c# – CIL – 拳击/拆箱与Nullable全部内容,希望文章能够帮你解决c# – CIL – 拳击/拆箱与Nullable所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1232787.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存