Search code examples
c#castinglanguage-design

Operator '??' cannot be applied to operands of type for child classes


The following code gives the error in the title on the second line in the Main function.

public class P {}

public class B : P {}

public class A : P {}

void Main()
{   
    P p = GetA()??GetB();
}

public A GetA() 
{
    return new A();
}

public B GetB()
{
    return new B();
}

A simple tweak to the line like these

    p = (P)GetA()??GetB();
    or
    p = GetA()??(P)GetB();

works.

I'm curious why the compiler doesn't understand that both are child classes of the left hand side container and allow the operation without the cast?


Solution

  • The type of the argument on the left hand side must be compatible with the type on the right hand side or vice versa. In other words, there must exist an implicit conversion from B to A or from A to B.

    var a = x ?? y;
    

    In the above, if there is an implicit conversion from y to x then the type of x becomes the type of the expression. if there is no implicit conversion from y to x, but there is an implicit conversion from x to y, then the type of y becomes the type of the expression. if no conversion exists in either direction, boom; compilation error. From the spec:

    The type of the expression a ?? b depends on which implicit conversions are available between the types of the operands. In order of preference, the type of a ?? b is A0, A, or B, where A is the type of a, B is the type of b (provided that b has a type), and A0 is the underlying type of A if A is a nullable type, or A otherwise. Specifically, a ?? b is processed as follows:

    • If A is not a nullable type or a reference type, a compile-time error occurs.

    • If A is a nullable type and an implicit conversion exists from b to A0, the result type is A0. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0, and this becomes the result. Otherwise, b is evaluated and converted to type A0, and this becomes the result.

    • Otherwise, if an implicit conversion exists from b to A, the result type is A. At run-time, a is first evaluated. If a is not null, a becomes the result. Otherwise, b is evaluated and converted to type A, and this becomes the result.

    • Otherwise, if b has a type B and an implicit conversion exists from A0 to B, the result type is B. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0 (unless A and A0 are the same type) and converted to type B, and this becomes the result. Otherwise, b is evaluated and becomes the result.

    • Otherwise, a and b are incompatible, and a compile-time error occurs.