[英]C# static classes and the is operator
在最近重构了一些涉及一些类重命名的代码之后,我的一些代码以惊人的方式破解了。 原因是失败的“是”运算符测试,我很惊讶不是编译器错误或警告。
完整的程序显示了这种情况:
static class ExtensionMethods {}
class Program {
static void Main() {
Test("Test");
}
public static bool Test(object obj)
{
return obj is ExtensionMethods;
}
}
鉴于ExtensionMethods是一个静态类,我本来期望“obj是ExtensionMethods”来引发某种警告。
当被测对象永远不能是提供的类型时,编译器将为“is”运算符发出警告, ((string)obj) is System.Uri
例如((string)obj) is System.Uri
。
我是否忘记了这实际上是一个有意义的测试的场景?
我很惊讶不是编译器错误或警告。
它应该是。 这是一种疏忽。
有许多错误,比如涉及静态类。 如果我没记错的话,甚至有一些奇怪的情况,Vladimir Reshetnikov发现可以进行类型推断的地方推断静态类型作为类型参数的约束。
显然,我之前见过的这个,从未得到修复。 为疏忽道歉。
我是否忘记了这实际上是一个有意义的测试的场景?
没有。
从C#3.0规范,第10.1.1.3节:
静态类可能不包含基类规范(第10.1.4节),并且不能显式指定基类或已实现接口的列表。 静态类隐式继承自类型对象。
因此编译器显然不会引发警告,因为它不知道is
总是返回false。 (静态类“是”的object
,因此,编译器不知道的object
“是”或“是”不是在编译时静态类)。现实情况可能不知道,或者至少能找到 ,但显然它没有专门的情况和检查。
我对此进行了一次破解,虽然我在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节),并且不能显式指定基类或已实现接口的列表。 静态类隐式继承自类型对象。
Eric Lippert从2013年开始的回答解释说这是Visual C#5.0编译器(以及一些早期版本)中的一个错误。 问题在于as
运算符,例如object bad = obj as ExtensionMethods;
。
在C#6.0(从2015年开始)以及之后,您会收到编译时错误(而不仅仅是警告):
错误CS7023:'is'或'as'运算符的第二个操作数可能不是静态类型'Xxxx'
但是,仅当您指定功能Strict时才会这样,有关如何执行此操作的详细信息 ,请参阅另一个线程 。
根据C#语言规范:
is运算符用于动态检查对象的运行时类型是否与给定类型兼容。 操作E的结果是T,其中E是表达式而T是类型,是一个布尔值,指示E是否可以通过引用转换,装箱转换或拆箱转换成功转换为类型T.
和
静态类可能不包含基类规范(第10.1.4节),并且不能显式指定基类或已实现接口的列表。 静态类隐式继承自类型对象。
由于它隐式地从System.Object
继承,因此编译器不发出警告是有意义的。
验证:
var staticBaseType = typeof(B).BaseType;
您将获得一个System.Object
作为基本类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.