I've seen a tweet about the null-coalescing
operator (which is right associative ):
From the SPEC:
For example, an expression of the form
a ?? b ?? c
is evaluated asa ?? (b ?? c)
So there was another guy which replied that it can be tested and verified with an example :
void Main()
{
Console.WriteLine ((P)null ?? (string)null ?? "b555");
}
public class P
{
public static implicit operator P(string b) {throw new Exception(b??"a");}
}
Result :
Exception: b555
But I didn't understand the behaviour.
Question
I already know that the ??
has a very low precedence but still :
(P)null
should evaluate first (higher precedence) !
But it seems that
a ?? (b ?? c)
Is evaluated first.
Why ?
In other words , it seems that these are the events:
(P)(null ?? ((string)null ?? "b555"))
And then :
(P)(null ?? "b555")
And then :
(P)"b555"
But I don't understand why (P)
is applied on all the coalesce expression and not on the null
( in (P)null
)
Why should your implicit conversion be applied to null
in (P)null
? (P)null
yields a null-reference statically typed to P
, why should any conversion from string be applied here? There is no mention of any string in (P)null
.
Note how the compiler statically types the following expressions¹:
((string)null ?? "b555") -> string
((P)null ?? ...some expression of type string...) -> P
Thus,
((P)null ?? (string)null ?? "b555") -> P
Now the expression can be resolved as follows:
(P)null
is null, so we look at the right-hand side.
((string)null ?? "b555")
yields the string "b555"
(no P involved).
((P)null ?? "b555")
results in a value of "b555"
. Since the static type of ((P)null ?? "b555")
is P
, b555
is implicitly converted to P
, triggering your implicit conversion.
We get an Exception "b555", as expected.
PS: If anyone is interested in a more detailed explanation in the form of a dialog, we have taken this topic to chat and here is the transcript.
¹ Proof:
public static void Main()
{
var x = ((P)null ?? "abc");
x.A(); // compiles
}
public class P
{
public static implicit operator P(string b) {throw new Exception(b??"a");}
public void A() {}
}