带有Nullable< T>的'=='的参数顺序 [英] Argument order for '==' with Nullable<T>

查看:111
本文介绍了带有Nullable< T>的'=='的参数顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The following two C# functions differ only in swapping the left/right order of arguments to the equals operator, ==. (The type of IsInitialized is bool). Using C# 7.1 and .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 是:

But the IL code for the second one seems much more complex. For example, B is:

  • 长36个字节(IL代码);
  • 调用其他功能,包括newobjinitobj;
  • 宣布四个本地人,而只宣布一个.
  • 36 bytes longer (IL code);
  • calls additional functions including newobj and initobj;
  • declares four locals versus just one.
[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. A B 之间在功能,语义或其他运行时方面是否有区别? (我们只对此处的正确性感兴趣,而不对性能感兴趣)
  2. 如果它们在功能上不是等价的,那么哪些运行时条件可以暴露出明显的差异?
  3. 如果它们功能等效项,则 B 在做什么(总是以与 A 相同的结果结束),以及触发了什么它的痉挛? B 是否具有永远无法执行的分支?
  4. 如果通过==左侧侧出现的差异(此处是引用表达式的属性与文字值)之间的差异来解释差异,您是否可以指出描述细节的C#规范.
  5. 是否有可靠的经验法则可用于在编码时预测膨胀的 IL ,从而避免创建它?
  1. Is there any functional, semantic, or other substantial runtime difference between A and B? (We're only interested in correctness here, not performance)
  2. If they are not functionally equivalent, what are the runtime conditions that can expose an observable difference?
  3. If they are functional equivalents, what is B doing (that always ends up with the same result as A), and what triggered its spasm? Does B have branches that can never execute?
  4. If the difference is explained by the difference between what appears on the left side of ==, (here, a property referencing expression versus a literal value), can you indicate a section of the C# spec that describes the details.
  5. Is there a reliable rule-of-thumb that can be used to predict the bloated IL at coding-time, and thus avoid creating it?

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

      BONUS. How does the respective final JITted x86 or AMD64 code for each stack up?


基于注释反馈的其他注释.首先,提出了第三个变体,但它给出与 A 相同的IL(对于DebugRelease构建).顺便说一句,新版本的 C#似乎比 A 更漂亮:

[edit]
Additional notes based on feedback in the comments. First, a third variant was proposed, but it gives identical IL as A (for both Debug and Release builds). Sylistically, however, the C# for the new one does seem sleeker than A:

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

这里也是每个功能的Release IL.请注意,对于Release IL, A / C B 的不对称性仍然很明显,因此原始问题仍然存在.

释放功能'A','C'的IL ...

Here also is the Release IL for each function. Note that the asymmetry A/C vs. B is still evident with the Release IL, so the original question still stands.

        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:

Lastly, a version using new C# 7 syntax was mentioned which seems to produce the cleanest IL of all:

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#规范/编译器,并且我对内部结构的理解受到限制.

So I was curious about the answer and took a look at the c# 6 specification (no clue where the c# 7 spec is hosted). Full disclaimer: I do not guarantee that my answer is correct, because I did not write the c# spec/compiler and my understanding of the internals is limited.

但我认为答案在于更好的功能成员.

Yet I think that the answer lies in the resultion of the overloadable == operator. The best applicable overload for == is determined by using the rules for better function members.

根据规格:

给出一个带有一组参数表达式{E1,E2, ...,En}和两个适用的带有参数的函数成员Mp和Mq 类型{P1,P2,...,Pn}和{Q1,Q2,...,Qn},Mp定义为 比Mq更好的功能成员

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

对于每个参数,从Ex到Qx的隐式转换并不更好 比从Ex到Px的隐式转换,并且至少要转换一个 参数,从Ex到Px的转换要好于转换 从Ex到Qx.

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.

What caught my eye is the argument list {E1, E2, .., En}. If you compare a Nullable<bool> to a bool the argument list should be something like {Nullable<bool> a, bool b}and for that argument list the Nullable<bool>.Equals(object o) method seems to be the best function, because it only takes one implicit conversion from bool to object.

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

However if you revert the order of the argument list to {bool a, Nullable<bool> b} the Nullable<bool>.Equals(object o) method no longer is the best function, because now you would have to convert from Nullable<bool> to bool in the first argument and then from bool to object in the second argument. That's why for case A a different overload is selected which seems to result in cleaner IL code.

再次说明,这是我对好奇心的满足,并且似乎符合c#规范.但是我还没有弄清楚如何调试编译器以了解实际情况.

Again this is an explanation that satisfies my own curiosity and seems to be in line with the c# spec. But I have yet to figure out how to debug the compiler to see what's actually going on.

这篇关于带有Nullable&lt; T&gt;的'=='的参数顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆