Note that there are some explanatory texts on larger screens.

plurals
  1. POCurious null-coalescing operator custom implicit conversion behaviour
    text
    copied!<p><strong>Note: this appears to have been fixed in <a href="https://github.com/dotnet/roslyn" rel="noreferrer">Roslyn</a></strong></p> <p>This question arose when writing my answer to <a href="https://stackoverflow.com/questions/6238074">this one</a>, which talks about the associativity of the <a href="http://msdn.microsoft.com/en-us/library/ms173224.aspx" rel="noreferrer">null-coalescing operator</a>.</p> <p>Just as a reminder, the idea of the null-coalescing operator is that an expression of the form</p> <pre><code>x ?? y </code></pre> <p>first evaluates <code>x</code>, then:</p> <ul> <li>If the value of <code>x</code> is null, <code>y</code> is evaluated and that is the end result of the expression</li> <li>If the value of <code>x</code> is non-null, <code>y</code> is <em>not</em> evaluated, and the value of <code>x</code> is the end result of the expression, after a conversion to the compile-time type of <code>y</code> if necessary</li> </ul> <p>Now <em>usually</em> there's no need for a conversion, or it's just from a nullable type to a non-nullable one - usually the types are the same, or just from (say) <code>int?</code> to <code>int</code>. However, you <em>can</em> create your own implicit conversion operators, and those are used where necessary.</p> <p>For the simple case of <code>x ?? y</code>, I haven't seen any odd behaviour. However, with <code>(x ?? y) ?? z</code> I see some confusing behaviour.</p> <p>Here's a short but complete test program - the results are in the comments:</p> <pre><code>using System; public struct A { public static implicit operator B(A input) { Console.WriteLine("A to B"); return new B(); } public static implicit operator C(A input) { Console.WriteLine("A to C"); return new C(); } } public struct B { public static implicit operator C(B input) { Console.WriteLine("B to C"); return new C(); } } public struct C {} class Test { static void Main() { A? x = new A(); B? y = new B(); C? z = new C(); C zNotNull = new C(); Console.WriteLine("First case"); // This prints // A to B // A to B // B to C C? first = (x ?? y) ?? z; Console.WriteLine("Second case"); // This prints // A to B // B to C var tmp = x ?? y; C? second = tmp ?? z; Console.WriteLine("Third case"); // This prints // A to B // B to C C? third = (x ?? y) ?? zNotNull; } } </code></pre> <p>So we have three custom value types, <code>A</code>, <code>B</code> and <code>C</code>, with conversions from A to B, A to C, and B to C.</p> <p>I can understand both the second case and the third case... but <em>why</em> is there an extra A to B conversion in the first case? In particular, I'd <em>really</em> have expected the first case and second case to be the same thing - it's just extracting an expression into a local variable, after all.</p> <p>Any takers on what's going on? I'm extremely hesistant to cry "bug" when it comes to the C# compiler, but I'm stumped as to what's going on...</p> <p>EDIT: Okay, here's a nastier example of what's going on, thanks to configurator's answer, which gives me further reason to think it's a bug. EDIT: The sample doesn't even need two null-coalescing operators now...</p> <pre><code>using System; public struct A { public static implicit operator int(A input) { Console.WriteLine("A to int"); return 10; } } class Test { static A? Foo() { Console.WriteLine("Foo() called"); return new A(); } static void Main() { int? y = 10; int? result = Foo() ?? y; } } </code></pre> <p>The output of this is:</p> <pre><code>Foo() called Foo() called A to int </code></pre> <p>The fact that <code>Foo()</code> gets called twice here is hugely surprising to me - I can't see any reason for the expression to be <em>evaluated</em> twice.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload