gpt4 book ai didi

c# - 带有 Nullable 的 '==' 的参数顺序

转载 作者:IT王子 更新时间:2023-10-29 04:45:02 29 4
gpt4 key购买 nike

以下两个 C# 函数的不同之处仅在于将参数的左/右顺序交换为 equals 运算符 ==。 (IsInitialized 的类型是bool)。使用 C# 7.1.NET 4.7

static void A(ISupportInitialize x)
{
if ((x as ISupportInitializeNotification)?.IsInitialized == true)
throw null;
}
static void B(ISupportInitialize x)
{
if (true == (x as ISupportInitializeNotification)?.IsInitialized)
throw null;
}

但是第二个的IL 代码 似乎要复杂得多。例如,B 是:

  • 长 36 个字节(IL 代码);
  • 调用其他函数,包括 newobjinitobj
  • 声明四个本地人与一个本地人。

函数“A”的 IL…

[0] bool flag
nop
ldarg.0
isinst [System]ISupportInitializeNotification
dup
brtrue.s L_000e
pop
ldc.i4.0
br.s L_0013
L_000e: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
L_0013: stloc.0
ldloc.0
brfalse.s L_0019
ldnull
throw
L_0019: ret

函数“B”的 IL…

[0] bool flag,
[1] bool flag2,
[2] valuetype [mscorlib]Nullable`1<bool> nullable,
[3] valuetype [mscorlib]Nullable`1<bool> nullable2
nop
ldc.i4.1
stloc.1
ldarg.0
isinst [System]ISupportInitializeNotification
dup
brtrue.s L_0018
pop
ldloca.s nullable2
initobj [mscorlib]Nullable`1<bool>
ldloc.3
br.s L_0022
L_0018: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
newobj instance void [mscorlib]Nullable`1<bool>::.ctor(!0)
L_0022: stloc.2
ldloc.1
ldloca.s nullable
call instance !0 [mscorlib]Nullable`1<bool>::GetValueOrDefault()
beq.s L_0030
ldc.i4.0
br.s L_0037
L_0030: ldloca.s nullable
call instance bool [mscorlib]Nullable`1<bool>::get_HasValue()
L_0037: stloc.0
ldloc.0
brfalse.s L_003d
ldnull
throw
L_003d: ret

问题

  1. AB 之间是否存在任何功能、语义或其他实质性运行时差异? (我们只对这里的正确性感兴趣,而不是性能)
  2. 如果它们在功能上相同,那么可以暴露可观察到差异的运行时条件是什么?
  3. 如果它们功能等价物,B在做什么(总是以与A相同的结果结束),以及什么触发了它的痉挛? B是否有永远无法执行的分支?
  4. 如果差异可以用 ==左侧 之间的差异来解释,(这里是属性引用表达式与文字值),可以您指定了描述详细信息的 C# 规范部分。
  5. 是否有可靠的经验法则可用于在编码时预测臃肿的 IL,从而避免创建它?

奖金。每个堆栈的最终 JITted x86AMD64 代码如何叠加?


[编辑]

根据评论中的反馈添加的附加说明。首先,提出了第三个变体,但它提供与 A 相同的 IL(对于 DebugRelease 构建)。然而,从语法上看,新版本的 C# 确实比 A 更时尚:

static void C(ISupportInitialize x)
{
if ((x as ISupportInitializeNotification)?.IsInitialized ?? false)
throw null;
}

这里还有每个函数的 Release IL。请注意,A/CB 的不对称性在 Release IL 中仍然很明显,因此原始问题依然屹立不倒。

为功能“A”、“C”…发布 IL

        ldarg.0
isinst [System]ISupportInitializeNotification
dup
brtrue.s L_000d
pop
ldc.i4.0
br.s L_0012
L_000d: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
brfalse.s L_0016
ldnull
throw
L_0016: ret

为功能“B”发布 IL…

[0] valuetype [mscorlib]Nullable`1<bool> nullable,
[1] valuetype [mscorlib]Nullable`1<bool> nullable2
ldc.i4.1
ldarg.0
isinst [System]ISupportInitializeNotification
dup
brtrue.s L_0016
pop
ldloca.s nullable2
initobj [mscorlib]Nullable`1<bool>
ldloc.1
br.s L_0020
L_0016: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
newobj instance void [mscorlib]Nullable`1<bool>::.ctor(!0)
L_0020: stloc.0
ldloca.s nullable
call instance !0 [mscorlib]Nullable`1<bool>::GetValueOrDefault()
beq.s L_002d
ldc.i4.0
br.s L_0034
L_002d: ldloca.s nullable
call instance bool [mscorlib]Nullable`1<bool>::get_HasValue()
L_0034: brfalse.s L_0038
ldnull
throw
L_0038: ret

最后,提到了一个使用新的 C# 7 语法的版本,它似乎产生了所有 IL 中最干净的:

static void D(ISupportInitialize x)
{
if (x is ISupportInitializeNotification y && y.IsInitialized)
throw null;
}

为函数“D”释放 IL…

[0] class [System]ISupportInitializeNotification y
ldarg.0
isinst [System]ISupportInitializeNotification
dup
stloc.0
brfalse.s L_0014
ldloc.0
callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
brfalse.s L_0014
ldnull
throw
L_0014: ret

最佳答案

所以我对答案很好奇并查看了 c# 6 规范(不知道托管 c# 7 规范的位置)。完全免责声明:我不保证我的回答是正确的,因为我没有编写 c# 规范/编译器并且我对内部的理解是有限的。

但我认为答案在于 overloadable 的结果==运算符(operator)。 == 的最佳适用过载通过使用 better function members 的规则确定.

来自规范:

Given an argument list A with a set of argument expressions {E1, E2, ..., En} and two applicable function members Mp and Mq with parameter types {P1, P2, ..., Pn} and {Q1, Q2, ..., Qn}, Mp is defined to be a better function member than Mq if

for each argument, the implicit conversion from Ex to Qx is not better than the implicit conversion from Ex to Px, and for at least one argument, the conversion from Ex to Px is better than the conversion from Ex to Qx.

引起我注意的是参数列表 {E1, E2, .., En} .如果你比较 Nullable<bool>bool参数列表应该类似于 {Nullable<bool> a, bool b}并为该参数列出 Nullable<bool>.Equals(object o)方法似乎是最好的函数,因为它只需要从 bool 进行一次隐式转换。至 object .

但是,如果您将参数列表的顺序恢复为 {bool a, Nullable<bool> b} Nullable<bool>.Equals(object o)方法不再是最好的功能,因为现在你必须从 Nullable<bool> 转换至 bool在第一个参数中,然后来自 boolobject在第二个参数中。这就是为什么对于案例 A 选择了不同的重载,这似乎导致了更清晰的 IL 代码。

同样,这是满足我自己好奇心的解释,似乎符合 c# 规范。但我还没有弄清楚如何调试编译器以查看实际发生的情况。

关于c# - 带有 Nullable<T> 的 '==' 的参数顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45751945/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com