考虑以下简单程序:
static class Program
{
static void Main()
{
}
static void Method(short? x)
{
const int y = 50; // note: is Int32, but is const and within Int16 range
var z = x ?? y; // note: var keyword used; IDE is confused about the type!
TakeOnlyInt16(z);
z.OnThisInt16();
}
static void TakeOnlyInt16(short a)
{
}
static void OnThisInt16(this short a)
{
}
}
这个程序绝对没有问题,它的编译没有问题。(您可以运行它,可能包括从Main
调用Method
。
但是,Visual Studio IDE 对局部变量的类型有错误的印象 z
。它似乎认为z
是一个Int32
,而实际上它是一个Int16
(又名 short
在 C# 中(。该问题至少在三种情况下显示:
当您将鼠标悬停"在
var
关键字上(按住(关键字上时,它会在工具提示中显示Int32
。这是错误的。当您将文本(键盘(光标移动到语句内
TakeOnlyInt16(z);
Method
内部时,它会在此语句的左下角显示一个小提示,提供"生成Program
中TakeOnlyInt16
的方法存根"。这是错误的,因为该方法显然已经存在。但它似乎认为已经存在的过载是错误的。short
和int
.当你在
Method
里面输入z.
(zed dot(时,Int32
的成员会出现在智能感知中。请注意,CompareTo
的重载是Int32
声明的重载,而不是Int16
声明的重载。此外,当您键入z.
时,智能感知成员列表中缺少扩展方法。
希望你理解我的解释,没有截图。
问题:这个错误从何而来?是众所周知的吗?它是否也在旧版本的Visual Studio中?
我在VS2013中尝试过这个。
根据 C# 参考,空合并运算符 (??(
用于定义可为 null 的值类型的默认值或 引用类型。如果操作数是 不为空;否则,它将返回正确的操作数。
如果右操作数是int
,而左操作数(当不为空时(是short
,编译器必须在int
和short
之间进行选择。而且,由于short
可以隐式转换为int
(反之亦然(,编译器可以正确确定此表达式的结果是int
类型。
还是不服气?为什么不能反过来呢?嗯,让我们看看 C# 语言规范 7.13 是怎么说的:
表达式 a ?? b 的类型取决于操作数上可用的隐式转换。按优先顺序,a的类型??b 是 A0、A 或 B,其中 A 是 a 的类型(假设 a 具有类型(,B 是 b 的类型(假设 b 具有类型(,如果 A 是可为空的类型,则 A0 是 A 的基础类型,否则为 A。
如果您仍然想忽略"可用的隐式转换"部分,因为这可能会导致认为它应该是 A0(短(,让我们继续阅读规范:
具体来说,a ?? b 的处理方式如下:
•如果 A 存在且不是可为 null 的类型或引用类型,则会发生编译时错误。
•如果 b 是动态表达式,则结果类型为动态。在运行时,首先评估 a。如果 a 不为 null,则 a 将转换为动态,这将成为结果。否则,将计算 b,这将成为结果。
•否则,如果 A 存在且为可为空的类型,并且存在从 b 到 A0 的隐式转换,则结果类型为 A0。在运行时,首先评估 a。如果 a 不为 null,则 a 解开包装为类型 A0,这将成为结果。否则,b 被计算并转换为类型 A0,这将成为结果。 注意:情况并非如此,没有从 b(int(到 A0(短(的转换
•否则,如果 A 存在,并且存在从 b 到 A 的隐式转换,则结果类型为 A。在运行时,首先评估 a。如果 a 不为 null,则 a 变为结果。否则,b 被计算并转换为类型 A,这将成为结果。
•否则,如果 b 具有类型 B,并且存在从 a 到 B 的隐式转换,则结果类型为 B。在运行时,首先评估 a。如果 a 不为 null,则 a 被解开包装为类型 A0(如果 A 存在且可为 null(并转换为类型 B,这将成为结果。否则,b 被计算并成为结果。 注意:情况是这种情况
•否则,a 和 b 不兼容,并发生编译时错误。
所以,不,编译器没有错误。你做了一个错误的假设。
编译器看到:
const int y = 50; // note: is Int32, but is const and within Int16 range
var z = x ?? y; // note: var keyword used; IDE is confused about the type!
并且知道y
属于int
型,无论它是否const
。在这种情况下,假设z
是Int32
,因为它是较大的类型。
如果您想制作 short
型z
,请尝试:
const int y = 50; // note: is Int32, but is const and within Int16 range
var z = x ?? (short)y; // note: var keyword used; IDE is confused about the type!
这是一个编译器优化,也许编辑器无法显示。
例如:
short? x = 44;
const int y = int.MaxValue;
var z = x ?? y;
var t = z.GetType(); //Int32
在这种情况下,编译器意识到z
最终可能包含不适合 Int16 的值,因此它将z
声明为 Int32。
在另一个示例中:
short? x = 44;
const int y = 33;
var z = x ?? y;
var t = z.GetType(); //Int16
编译器意识到z
将包含一个始终适合 Int16 的值,因此它将z
声明为 Int16