title: Argument order for '==' with Nullable<T>
body: <p>The following two <code>C#</code> functions differ only in swapping the left/right order of arguments to the <strong>equals</strong> operator, <code>==</code>. (The type of <code>IsInitialized</code> is <code>bool</code>). Using <strong>C# 7.1</strong> and <strong>.NET 4.7</strong>.</p>
  <pre><code>static void A(ISupportInitialize x)
  {
      if ((x as ISupportInitializeNotification)?.IsInitialized == true)
          throw null;
  }
  </code></pre>
  
  <pre><code>static void B(ISupportInitialize x)
  {
      if (true == (x as ISupportInitializeNotification)?.IsInitialized)
          throw null;
  }
  </code></pre>
  <p>But the <strong>IL code</strong> for the second one seems much more complex. For example, <strong>B</strong> is:</p>
  <ul>
  <li>36 bytes longer (IL code);</li>
  <li>calls additional functions including <code>newobj</code> and <code>initobj</code>;</li>
  <li>declares four locals versus just one.</li>
  </ul>
  <h4>IL for function 'A'…</h4>
  <pre class="lang-none prettyprint-override"><code>[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
  </code></pre>
  <h4>IL for function 'B'…</h4>
  <pre class="lang-none prettyprint-override"><code>[0] bool flag,
  [1] bool flag2,
  [2] valuetype [mscorlib]Nullable`1&lt;bool&gt; nullable,
  [3] valuetype [mscorlib]Nullable`1&lt;bool&gt; 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&lt;bool&gt;
          ldloc.3
          br.s L_0022
  L_0018: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
          newobj instance void [mscorlib]Nullable`1&lt;bool&gt;::.ctor(!0)
  L_0022: stloc.2
          ldloc.1
          ldloca.s nullable
          call instance !0 [mscorlib]Nullable`1&lt;bool&gt;::GetValueOrDefault()
          beq.s L_0030
          ldc.i4.0
          br.s L_0037
  L_0030: ldloca.s nullable
          call instance bool [mscorlib]Nullable`1&lt;bool&gt;::get_HasValue()
  L_0037: stloc.0
          ldloc.0
          brfalse.s L_003d
          ldnull
          throw
  L_003d: ret
  </code></pre>
  <p> </p>
  <h2>Questions</h2>
  <ol>
  <li>Is there any functional, semantic, or other substantial runtime difference between <strong>A</strong> and <strong>B</strong>? (We're only interested in correctness here, not performance)</li>
  <li>If they are <em>not</em> functionally equivalent, what are the runtime conditions that can expose an observable difference?</li>
  <li>If they <em>are</em> functional equivalents, what is <strong>B</strong> doing (that always ends up with the same result as <strong>A</strong>), and what triggered its spasm? Does <strong>B</strong> have branches that can never execute?</li>
  <li>If the difference is explained by the difference between what appears on the <strong>left</strong> side of <code>==</code>, (here, a property referencing expression versus a literal value), can you indicate a section of the C# spec that describes the details.</li>
  <li>Is there a reliable rule-of-thumb that can be used to predict the bloated <strong>IL</strong> at coding-time, and thus avoid creating it?</li>
  </ol>
  <p>      BONUS. How does the respective final JITted <code>x86</code> or <code>AMD64</code> code for each stack up?</p>
  <hr />
  <h2>[edit]</h2>
  <p>Additional notes based on feedback in the comments. First, a third variant was proposed, but it gives identical IL as <strong>A</strong> (for both <code>Debug</code> and <code>Release</code> builds). Sylistically, however, the <strong>C#</strong> for the new one does seem sleeker than <strong>A</strong>:</p>
  <pre><code>static void C(ISupportInitialize x)
  {
      if ((x as ISupportInitializeNotification)?.IsInitialized ?? false)
          throw null;
  }
  </code></pre>
  <p>Here also is the <code>Release</code> IL for each function. Note that the asymmetry <strong>A</strong>/<strong>C</strong> vs. <strong>B</strong> is still evident with the <code>Release</code> IL, so the original question still stands.</p>
  <h4>Release IL for functions 'A', 'C'…</h4>
  <pre class="lang-none prettyprint-override"><code>        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
  </code></pre>
  <h4>Release IL for function 'B'…</h4>
  <pre class="lang-none prettyprint-override"><code>[0] valuetype [mscorlib]Nullable`1&lt;bool&gt; nullable,
  [1] valuetype [mscorlib]Nullable`1&lt;bool&gt; nullable2
          ldc.i4.1
          ldarg.0
          isinst [System]ISupportInitializeNotification
          dup
          brtrue.s L_0016
          pop
          ldloca.s nullable2
          initobj [mscorlib]Nullable`1&lt;bool&gt;
          ldloc.1
          br.s L_0020
  L_0016: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
          newobj instance void [mscorlib]Nullable`1&lt;bool&gt;::.ctor(!0)
  L_0020: stloc.0
          ldloca.s nullable
          call instance !0 [mscorlib]Nullable`1&lt;bool&gt;::GetValueOrDefault()
          beq.s L_002d
          ldc.i4.0
          br.s L_0034
  L_002d: ldloca.s nullable
          call instance bool [mscorlib]Nullable`1&lt;bool&gt;::get_HasValue()
  L_0034: brfalse.s L_0038
          ldnull
          throw
  L_0038: ret
  </code></pre>
  <p>Lastly, a version using new <strong>C# 7</strong> syntax was mentioned which seems to produce the cleanest IL of all:</p>
  <pre><code>static void D(ISupportInitialize x)
  {
      if (x is ISupportInitializeNotification y &amp;&amp; y.IsInitialized)
          throw null;
  }
  </code></pre>
  <h4>Release IL for function 'D'…</h4>
  <pre class="lang-none prettyprint-override"><code>[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
  </code></pre>