最近重构了一些代码,其中涉及一些类重命名,我的一些代码以惊人的方式破解。原因是失败的“是”运算符测试,我很惊讶不是编译器错误或警告。
这个完整的程序显示了这种情况:
static class ExtensionMethods {}
class Program {
static void Main() {
Test("Test");
}
public static bool Test(object obj)
{
return obj is ExtensionMethods;
}
}
鉴于ExtensionMethods是一个静态类,我本来期望“obj是ExtensionMethods”来引发某种警告。
当被测对象永远不能是提供的类型((string)obj) is System.Uri
时,编译器将对“is”运算符发出警告。
我是否忘记了这实际上是一次有意义的测试?
答案 0 :(得分:11)
我很惊讶不是编译器错误或警告。
应该是的。这是一种疏忽。
有许多错误,比如涉及静态类。如果我没记错的话,甚至有一些奇怪的情况,Vladimir Reshetnikov发现可以进行类型推断的地方推断静态类型作为类型参数的约束。
显然,我之前见过的这个,从未得到修复。为疏忽道歉。
我是否忘记了这实际上是一次有意义的测试?
没有
答案 1 :(得分:4)
从C#3.0规范,第10.1.1.3节:
静态类可能不包含基类规范(第10.1.4节),并且不能显式指定基类或已实现接口的列表。 静态类隐式继承自类型对象。
因此,编译器显然不会引发警告,因为它不知道is
将始终返回false。 (静态类“是”object
,因此编译器不知道object
“是”或“在编译时不是静态类。”实际上它可能 知道,或者至少可以找到,但显然它并不专门处理这种情况并检查。
答案 2 :(得分:1)
我对此采取了一个破解,虽然我在MSDN参考中找不到这个,但似乎操作符依赖于实例化一个能够检查的类型。由于静态类无法实例化(因为静态类是在编译时在程序堆栈上创建的对象)...
例如,如果您执行以下操作,则会出现以下错误:“无法声明静态类型的变量”
ExtensionMethods ex;
如果您执行以下操作,则会出现以下错误:“无法创建静态类的实例”
ExtensionMethods ex2 = new ExtensionMethods();
为了演示这个问题,这里有一个完整的程序,显示了is运算符。
static class ExtensionMethods { }
// notice non-static
class AnotherNonStaticExtensionMethod { }
class Program
{
static void Main(string[] args)
{
Debug.WriteLine(Test(new AnotherNonStaticExtensionMethod()).ToString());
Debug.WriteLine(Test("Test").ToString());
Debug.WriteLine(Test(4).ToString());
}
public static bool Test(object obj)
{
if (obj is ExtensionMethods)
{
return true;
}
else if (obj is AnotherNonStaticExtensionMethod)
{
return true;
}
else
{
return false;
}
}
}
以下是输出:
True
False
False
is对象能够使用第一个语句检查可实例化的类 - 从而使我相信is运算符依赖于它。我希望有人可以证实这一点吗?
由NominSim提供::
从C#3.0规范,第10.1.1.3节:
静态类可能不包含基类规范(第10.1.4节) 并且无法显式指定基类或已实现的列表 接口。静态类隐式继承自类型对象。
答案 3 :(得分:1)
Eric Lippert在2013年的回答中解释说,这是Visual C#5.0编译器(以及一些早期版本)中的一个错误。 as
运算符也存在问题,例如object bad = obj as ExtensionMethods;
。
在C#6.0(从2015年开始)以及之后,您会收到编译时错误(而不仅仅是警告):
错误CS7023:'的第二个操作数是'或者' as'操作者 可能不是静态类型' Xxxx'
但是,只有在指定功能Strict时才会出现这种情况,请参阅another thread for details on how to do that。
答案 4 :(得分:0)
根据C#语言规范:
is运算符用于动态检查对象的运行时类型是否与给定类型兼容。操作E的结果是T,其中E是表达式而T是类型,是一个布尔值,指示E是否可以通过引用转换,装箱转换或拆箱转换成功转换为类型T.
和
静态类可能不包含基类规范(第10.1.4节),并且不能显式指定基类或已实现接口的列表。静态类隐式继承自类型对象。
由于它隐式地从System.Object
继承,因此编译器没有发出警告是有道理的。
验证
var staticBaseType = typeof(B).BaseType;
您将获得System.Object
作为基本类型。