考虑以下简单程序:
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)
{
}
}
这个程序绝对没有错,编译没有问题。
(并且您可以运行它,可能包括从Method
拨打Main
。)
但是,Visual Studio IDE对局部变量z
的类型有错误的印象。当z
实际上是Int32
(C#中的a.k.a。Int16
时,似乎认为short
是var
。问题至少表现在三种情况:
当您“悬停”{保持鼠标悬停在Int32
关键字上时,它会在工具提示中显示TakeOnlyInt16(z);
。那是错的。
当您将文本(键盘)光标移动到Method
内的语句TakeOnlyInt16
内时,它会在此语句的左下角显示一个小提示,提供“生成方法” Program
中short
的存根。这是错误的,因为该方法显然已经存在。但它似乎认为已经存在的过载是错误的。 int
和z.
。
当您在Method
内输入Int32
(zed dot)时,CompareTo
的成员会出现在 intellisense 中。请注意Int32
的重载是Int16
声明的重载,而不是z.
声明的重载。此外,当您键入{{1}}时,intellisense成员列表中缺少扩展方法。
希望您在没有屏幕截图的情况下理解我的解释。
问题:这个错误来自哪里?它是众所周知的吗?它是否也在旧版本的Visual Studio中?
我在VS2013中试过这个。
答案 0 :(得分:2)
According to C# reference,null-coalescing运算符(??)
用于定义可空值类型的默认值 参考类型。如果操作数是,则返回左操作数 不为空;否则它返回正确的操作数。
如果右侧操作数为int
,而左侧操作数为非空时为short
,则编译器必须在int
和short
之间进行选择。并且,由于short
可以隐式转换为int
(而不是反之亦然),编译器会正确地确定此表达式的结果是int
类型。
还是不相信?为什么不能反过来呢?嗯,让我们看看C# Language Specification,7.13:
表达式的类型a ?? b取决于操作数上可用的隐式转换。按优先顺序排列,类型为?? b是A0,A或B,其中A是a的类型(假设a具有类型),B是b的类型(假设b具有类型),并且A0是A的基础类型,如果A是可以为空的类型,否则为A.
如果你仍然想忽略“隐含转换可用”部分,因为这可能会导致认为它应该是A0(短),让我们继续阅读规范:
具体来说,?? b按如下方式处理:
•如果A存在且不是可空类型或引用类型,则会发生编译时错误。
•如果b是动态表达式,则结果类型是动态的。在运行时,首先评估a。如果a不为null,则a将转换为dynamic,这将成为结果。否则,将评估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存在并且可以为空)并转换为类型B,这将成为结果。否则,b将被评估并成为结果。 注意:就是这种情况
•否则,a和b不兼容,并发生编译时错误。
所以,不,编译器没有错误。你做出了错误的假设。
答案 1 :(得分:0)
编译器看到:
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
,因为它是更大的类型。
如果您希望z
类型为short
,请尝试:
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!
答案 2 :(得分:0)
编译器优化可能是编辑器无法显示的。
例如:
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