- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如果我理解 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.0
IL_0001: isinst valuetype [mscorlib]System.Nullable`1<!!T>
IL_0006: unbox.any valuetype [mscorlib]System.Nullable`1<!!T>
IL_000b: stloc.1
IL_000c: ldloca.s 1
IL_000e: call instance !0 valuetype [mscorlib]System.Nullable`1<!!T>::GetValueOrDefault()
IL_0013: stloc.0
IL_0014: ldloca.s 1
IL_0016: call instance bool valuetype [mscorlib]System.Nullable`1<!!T>::get_HasValue()
IL_001b: brfalse.s IL_003c
IL_001d: ldstr "Argument is {0}: {1}"
IL_0022: ldtoken !!T
IL_0027: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_002c: ldloc.0
IL_002d: box !!T
IL_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.0
IL_0001: isinst !!T
IL_0006: brfalse.s IL_002c
IL_0008: ldstr "Argument is {0}: {1}"
IL_000d: ldtoken !!T
IL_0012: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0017: ldarg.0
IL_0018: unbox.any !!T
IL_001d: box !!T
IL_0022: call string [mscorlib]System.String::Format(string, object, object)
IL_0027: call void [mscorlib]System.Console::WriteLine(string)
IL_002c: ret
我认为正在发生什么:查看第一种方法的 CIL,似乎 (1) 检查参数是否为 [boxed?] Nullable<T>
,如果是,则将其压入堆栈,否则为 null
,(2) 将其拆箱(如果是 null
怎么办?),(3) 尝试获取其值,否则为 default(T)
,(4) 然后检查它是否具有值与否,如果没有,则分支出去。第二种方法的 CIL 非常简单,它只是尝试对参数进行拆箱。
如果两种代码的语义相同,为什么前一种情况涉及拆箱到 Nullable<T>
而前一种情况“只是拆箱”?其次,在第一个 CIL 中,如果对象参数是一个盒装的 int
,我目前认为这正是它在 jar 上所说的(即盒装 int
而不是盒装 Nullable<int>
),那么 isinst
指令不会总是失败? Nullable<T>
是否在 CIL 级别得到特殊处理?
更新:在手写一些 MSIL 之后,似乎 object
确实是一个装箱的 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
转换(编译为 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,
object)
IL_002f: ret
}
除了调用 GetValueOrDefault 之外,这正是问题中的代码,因为我没有获得可空实例的实际值。
可空类型不能直接装箱或拆箱,只能通过其基础值或作为普通空值。第一个 isinst
确保其他类型不会产生异常(我想 isinst !!T
也可以使用),只是一个空引用。 unbox.any
操作码然后根据引用形成一个可为空的实例,然后照常使用。该指令也可以写成空检查并自行形成可为空的实例,但这样会更短。
对于 is T
,C# 7 使用第二种方式,因此如果 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,
object)
IL_002e: ret
}
再次与原始方法相当一致。
关于c# - CIL - 装箱/拆箱与可空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43684315/
我正在查看 CIL 中的各种操作码,我发现了很多类似的代码,例如 Beq,Beq_S Bge,Bge_S Bgt,Bgt_S Br,Br_S 其中 S 应该是“短格式”。这意味着什么?? 最佳答案 指
查看 monodis 生成的代码,我看到对 Console.WriteLine 的调用被转换为 call void class [mscorlib]System.Console::WriteLine。
使用 Frama-C API 中定义的 Cil_datatypes 模块,我尝试用谓词 (Cil_datatype) 中的新术语替换术语 (Cil_datatype)。为此,我需要使用一个函数映射一个
由于各种原因,我正在学习 CIL,似乎类的定义通常类似于: .class public auto ansi beforefieldinit [...] extends [mscorlib]Syste
在 Microsoft IL 中,要对值类型调用方法,您需要间接引用。假设我们有一个名为“il”的 ILGenerator 并且当前我们在堆栈顶部有一个 Nullable,如果我们想检查它是否有值,那
让我们假设以下 CIL 程序: .assembly extern mscorlib {} .assembly Program {} .method private static void Main()
这可能是一个基本问题,但是CIL的含义是什么? 我知道CIL是与平台和CPU无关的中间语言,但是 有人对此进行了解释以及CPU不可知论的含义将对您有所帮助。 最佳答案 如果是platform agno
我对值类型和 CIL 评估堆栈有点困惑。 ECMA-335, Partition III, §1.1 说 CLI 处理这些“基本 CLI 类型”: A subset of the full numer
如果应用程序采用 MSIL 格式,为什么 Visual Studio 会将应用程序编译为可执行文件?不应该像java那样把代码编译成.class文件吗? 最佳答案 .NET“可执行文件”实际上是一个很
是否有任何可读的最新(.net 4)MSIL 引用手册? 最佳答案 有一堆可下载的引用文档here . 包括 2006 年 6 月的 ECMA-335 公共(public)语言基础结构文档 (pdf)
.Net 运行时是否对 CIL 指令的原子性做出任何保证?如果是这样,它们是什么?所有 CIL 指令都是原子的吗?或者它是否取决于 JIT 编译器生成的机器代码的原子性,因此太特定于平台而无法做出任何
我有一个基于自定义堆栈的语言,我正在尝试将其编译为 CIL,以便可以对其进行 JIT 编译。该语言本身相当简单,因为它只有整数和 bool 值。然而,每种数据类型都有一个专用的堆栈。该语言本身是一个命
假设我的解决方案中有一个 C# Windows 类库,我在 VS2010 IDE 中构建它。我的 bin 目录中的输出是 X.dll 1) X.dll现阶段不包含MSIL,而是“压缩字节码”。 这是真
是否有任何 .NET CIL(又名 MSIL)ANTLR 语法? 最佳答案 根据this publication , Microsoft 在Microsoft Visual Studio 安装文件as
什么是获得可移植可执行文件的良好、人类可读形式的 CLI 的好程序?我不想要一个完整的反汇编程序,因为我想了解它们的工作原理(或者,就我而言,不工作)。 最佳答案 我推荐reflector . 它可以
我可以将指令编译为字节码,甚至可以轻松执行它们,但我发现提取 CIL 的唯一函数是 GetILAsByteArray,顾名思义,它只返回字节而不是 CIL 指令。 那么如何在 .NET 上以编程方式反
我理解以下 C# 代码: var evens = from n in nums where n % 2 == 0 select n; 编译为: var evens = nums.Where(n =>
尝试使用 ildasm 深入研究 CIL 代码,很明显 CIL 本身正在基于堆栈工作以支持如下表达式 IL_0001: ldc.i4.s 13 ; 1f 0d IL_0003: s
我在 CIL 中定义了一个字段,如下所示: .field public int32 modopt(void*) fld 我把它编译成一个程序集。现在我把它改成: .field public int32
是否可以编写将任何类型(值和引用)的实例转换为 System.String 的通用 CIL 指令? 特别是,我对将这些指令注入(inject)方法的 Mono.Cecil 代码感兴趣。 分析一个通用方
我是一名优秀的程序员,十分优秀!