Null-Coallescing运算符 – 为什么选择铸造?

任何人都可以请告诉我为什么以下第一个语句抛出编译错误而第二个不抛出?

NewDatabase.AddInParameter(NewCommand, "@SomeString", DbType.String, SomeString ?? DBNull.Value); // <-- Throws compilation error! NewDatabase.AddInParameter(NewCommand, "@SomeString", DbType.String, (object)(SomeString) ?? DBNull.Value); // <-- Compiles! 

我尝试过其他可以为空的类型,比如byte? 并得到了相同的结果。 任何人都可以告诉我为什么我需要先投射到物体?

您需要告诉编译器要使用的类型。 空合并运算符的结果类型必须与其中一个操作数类型(或第一个操作数的基础类型,如果它是可以为空的值类型,在某些情况下)相同。 它不会试图找到“两个操作数都可以转换为”的最具体类型或类似的东西。

有关如何在空合并运算符中定义语言的详细信息,请参阅C#4语言规范 ,第7.13节:

表达式的类型a ?? b a ?? b取决于操作数上可用的隐式转换。 按优先顺序排列,类型为a ?? b a ?? bA0AB ,其中A是a的类型(假设a具有类型), Bb的类型(假设b具有类型),并且A0A的基础类型,如果A是可以为空的类型,否则为A

第一个示例失败,因为SomeStringDBValue.Null不是隐式可互换的类型 。

这是因为null-coalescing操作符右侧的类型必须可以隐式转换为左侧的类型(反之亦然)。 对于您的第一个示例,涉及的类型是stringDBNull 。 这些类型不相关,因此转换失败。

DBValue.Null不是字符串; 它是一个对象。 .NET不会在表达式中隐式地转换为Object; 必须明确告诉你,你期待一个Object结果。

因为表达式需要有一个返回类型。 由于StringDbValue不能相互转换,编译器无法确定您想要哪种类型的返回。 当你转换为Object时,你给编译器一个它可以强制转换的类型。