Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/266.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么C#编译器会翻译这个!=就好像它是a>;比较?_C#_Cil_Il_Notnull_Binary Operators - Fatal编程技术网

为什么C#编译器会翻译这个!=就好像它是a>;比较?

为什么C#编译器会翻译这个!=就好像它是a>;比较?,c#,cil,il,notnull,binary-operators,C#,Cil,Il,Notnull,Binary Operators,我完全是偶然发现C#编译器将此方法转换为: static bool IsNotNull(object obj) { return obj != null; } ……在这方面: …或者,如果您更喜欢查看反编译的C代码: 那怎么会是=被翻译为“”?简短回答: IL中没有“比较不相等”指令,因此C#=运算符没有精确的对应关系,无法按字面翻译 但是,有一条“比较相等”指令(ceq,与=运算符直接对应),因此在一般情况下,x!=y被翻译成稍微长一点的等价物(x==y==false) IL(cgt

我完全是偶然发现C#编译器将此方法转换为:

static bool IsNotNull(object obj)
{
    return obj != null;
}
……在这方面:

…或者,如果您更喜欢查看反编译的C代码:

那怎么会是
=被翻译为“
”?

简短回答: IL中没有“比较不相等”指令,因此C#
=运算符没有精确的对应关系,无法按字面翻译

但是,有一条“比较相等”指令(
ceq
,与
=
运算符直接对应),因此在一般情况下,
x!=y
被翻译成稍微长一点的等价物
(x==y==false

IL(
cgt
)中还有一条“compare Better Thor”(比较大于)指令,允许编译器采用某些快捷方式(即生成较短的IL代码),其中一条是将对象与null进行不平等比较,
obj!=null
,将其翻译为“
obj>null

让我们更详细地讨论一下。 如果IL中没有“compare not equal”指令,那么编译器将如何翻译以下方法

static bool IsNotEqual(int x, int y)
{
    return x != y;
}
如上所述,编译器将打开
x!=y
转换为
(x==y)=false

.method private hidebysig static bool IsNotEqual(int32 x, int32 y) cil managed 
{
    ldarg.0   // x
    ldarg.1   // y
    ceq
    ldc.i4.0  // false
    ceq       // (note: two comparisons in total)
    ret
}
事实证明,编译器并不总是生成这种相当冗长的模式。让我们看看用常量0替换
y
时会发生什么:

static bool IsNotZero(int x)
{
    return x != 0;
}
产生的IL比一般情况下短一些:

.method private hidebysig static bool IsNotZero(int32 x) cil managed 
{
    ldarg.0    // x
    ldc.i4.0   // 0
    cgt.un     // (note: just one comparison)
    ret
}
编译器可以利用有符号整数存储在中的事实(其中,如果生成的位模式被解释为无符号整数-这就是
.un
的意思-0具有最小的可能值),因此它将
x==0
转换为
未选中((uint)x)>0

事实证明,编译器可以对
null
执行同样的不等式检查:

static bool IsNotNull(object obj)
{
    return obj != null;
}
编译器生成的IL与
IsNotZero的IL几乎相同:

.method private hidebysig static bool IsNotNull(object obj) cil managed 
{
    ldarg.0
    ldnull   // (note: this is the only difference)
    cgt.un
    ret
}
显然,编译器可以假定
null
引用的位模式是任何对象引用可能的最小位模式

该快捷方式在(第491页,作为表6-4“二进制比较或分支操作”的脚注)中明确提到:

允许并可在ObjectRefs(O)上验证
cgt.un。这通常用于将ObjectRef与null进行比较(没有“compare not equal”指令,否则这将是一个更明显的解决方案)


回答得很好,只有一个问题:二的补语在这里不相关。重要的是,有符号整数的存储方式应确保
int
范围内的非负值在
int
中的表示方式与在
uint
中的表示方式相同。这是一个比补码弱得多的要求。无符号类型从来没有任何负数,因此比较为零的比较操作不能将任何非零的数字视为小于零。与
int
的非负值相对应的所有表示都已被
uint
中的相同值占用,因此与
int
的负值相对应的所有表示都必须与
uint
大于
0x7fffff
的某个值相对应,但是它的值是多少并不重要。(实际上,真正需要做的就是在
int
uint
中用相同的方式表示零)@hvd:谢谢你的解释。你说得对,重要的不是两个人的互补;这是
cgt.un
int
视为
uint
而不改变底层位模式的要求和事实。(假设
cgt.un
首先尝试通过将所有负数映射到0来修复下溢。在这种情况下,显然无法用
>0
替换
!=0
)我发现使用
将对象引用与另一个对象引用进行比较是可验证的,这让我感到惊讶。这样就可以比较两个非空对象并得到一个布尔结果(这是非确定性的)。这不是内存安全问题,但感觉像是不干净的设计,不符合安全管理代码的一般精神。这种设计泄露了对象引用作为指针实现的事实。似乎是.NET CLI的设计缺陷。@usr:绝对是!本标准第III.1.1.4节指出“对象引用(O型)是完全不透明的”,并且“允许的唯一比较操作是相等和不相等……”可能是因为对象引用没有按照内存地址定义,所以本标准还注意在概念上将空引用与0分开(例如,参见
ldnull
initobj
newobj
的定义)。因此使用
cgt.un
比较对象引用与空引用在多个方面似乎与第III.1.1.4节相矛盾。
static bool IsNotNull(object obj)
{
    return obj != null;
}
.method private hidebysig static bool IsNotNull(object obj) cil managed 
{
    ldarg.0
    ldnull   // (note: this is the only difference)
    cgt.un
    ret
}