操作符& # 39;? ? & # 39;不能应用于子类类型的操作数
本文关键字:操作数 类型 应用于 操作符 不能 子类 | 更新日期: 2023-09-27 18:11:40
下面的代码给出了Main函数第二行标题中的错误。
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();
}
对行进行如下简单调整
p = (P)GetA()??GetB();
or
p = GetA()??(P)GetB();
。
我很好奇为什么编译器不明白这两个都是左侧容器的子类,并允许没有强制转换的操作?
左侧实参的类型必须与右侧的类型兼容,反之亦然。换句话说,必须存在从B
到A
或从A
到B
的隐式转换。
var a = x ?? y;
在上面的例子中,如果存在从y
到x
的隐式转换,则x
的类型成为表达式的类型。如果没有从y
到x
的隐式转换,但有从x
到y
的隐式转换,则y
的类型成为表达式的类型。如果两个方向都不存在转换,boom;编译错误。来自规范:
表达式的类型a ??B取决于操作数类型之间可用的隐式转换。按偏好排序,a ??b是A0、A或b,其中A是A的类型,b是b的类型(前提是b有类型),如果A是可空类型,A0是A的底层类型,否则为A。具体来说,a ??B的处理如下:
•如果A不是可空类型或引用类型,则发生编译时错误。
•如果A是可空类型,并且存在从b到A0的隐式转换,则结果类型为A0。在运行时,首先求值a。如果a不为空,则将a展开为类型A0,这就是结果。否则,b被求值并转换为类型A0,这就是结果。
•否则,如果存在从b到A的隐式转换,则结果类型为A。在运行时,首先求值A。如果a不为空,则结果为a。否则,b被求值并转换为类型A,这就是结果。
•否则,如果b具有类型b,并且存在从A0到b的隐式转换,则结果类型为b。在运行时,首先计算a。如果a不为空,则a被解封为类型A0(除非a和A0是相同的类型)并转换为类型B,这就是结果。否则,b被求值并成为结果。
•否则,a和b不兼容,并发生编译时错误。
我很好奇为什么编译器不明白这两个都是左侧容器的子类,并允许没有强制转换的操作?
因为这样就可以了:
public class SqlConnection : object {}
public class Random : object {}
public SqlConnection GetA() { return new SqlConnection(); }
public Random GetB() { return new Random(); }
void Main()
{
var p = GetA() ?? GetB();
}
操作符的操作数??应该是相同的类型:
P a = GetA();
P b = GetB();
P p = a ?? b;