C#静态类和is运算符

C#静态类和is运算符,c#,C#,在最近重构了一些代码(涉及一些类重命名)之后,我的一些代码以一种令人惊讶的方式崩溃了。原因是“is”操作符测试失败,我很惊讶不是编译器错误或警告 这个完整的程序显示了以下情况: static class ExtensionMethods {} class Program { static void Main() { Test("Test"); } public static bool Test(object obj) { re

在最近重构了一些代码(涉及一些类重命名)之后,我的一些代码以一种令人惊讶的方式崩溃了。原因是“is”操作符测试失败,我很惊讶不是编译器错误或警告

这个完整的程序显示了以下情况:

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


我是否忘记了一个场景,在这个场景中,这实际上是一个有意义的测试?

我对此进行了分析,尽管我在MSDN引用中找不到这个场景,但似乎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操作符依赖于它。我希望有人能证实这一点

诺明辛先生提供:

根据C#3.0规范第10.1.1.3节:

静态类可能不包括类基本规范(§10.1.4) 并且不能显式指定基类或已实现的 接口。静态类隐式继承自类型对象

根据C#3.0规范第10.1.1.3节:

静态类可能不包括类基类规范(§10.1.4),也不能明确指定基类或已实现接口的列表静态类隐式继承自类型对象。

因此,编译器显然没有发出警告,因为它不知道
将始终返回false。(静态类“是”一个
对象,因此编译器在编译时不知道
对象“是”或“不是”一个静态类。)实际上,它可能知道,或者至少可以发现,但显然它没有专门化该情况并进行检查。

根据C语言规范:

var staticBaseType = typeof(B).BaseType;
is运算符用于动态检查对象的运行时类型是否与给定类型兼容。操作E的结果是T,其中E是表达式,T是类型,是一个布尔值,指示是否可以通过引用转换、装箱转换或取消装箱转换将E成功转换为类型T

静态类可能不包括类基类规范(§10.1.4),也不能明确指定基类或已实现接口的列表。静态类隐式继承自类型对象

由于它隐式继承自
System.Object
,因此编译器没有发出警告是有道理的

核查:

var staticBaseType = typeof(B).BaseType;
您将获得一个
System.Object
作为基本类型

我很惊讶不是编译器错误或警告

应该是的。这是一个疏忽

有许多类似的bug涉及到静态类。如果我没记错的话,Vladimir Reshetnikov甚至发现了一些奇怪的场景,在那里,可以通过类型推断推断出静态类型作为类型参数的绑定

显然,我以前见过的这个,从来没有被修复过。为疏忽道歉

我是否忘记了一个场景,在这个场景中,这实际上是一个有意义的测试


否。

埃里克·利珀特(Eric Lippert)在2013年的回答中解释说,这是Visual C#5.0编译器(以及一些早期版本)中的一个缺陷。
as
操作符也存在问题,例如
object bad=obj as ExtensionMethods

在C#6.0(从2015年起)和更高版本中,确实会出现编译时错误(而不仅仅是警告):

错误CS7023:“is”或“as”运算符的第二个操作数 可能不是静态类型“Xxxx”


但是,只有当您指定feature Strict时,这才是正确的,请参见。

FWIW,ReSharper捕捉到了这一点。鉴于传递到
Test
方法的
对象实际上没有指定编译时类型,我不知道编译器如何知道这一区别。@Robert,它不总是错误的,因为没有任何东西可以是静态类的实例吗?@Robert:当然,它是System.Object类型的。我希望编译器知道任何东西都不能是这种类型,因为这种类型是一个静态类,因此不可能有它的实例,除非我忘记了什么。显然,编译器没有检查它。和
系统。对象
就像O-阴性血液;虽然很明显,
String
不能是
System.Uri
,但运行时,
Object
不能是
System.Uri
,这一点并不清楚。
var staticBaseType = typeof(B).BaseType;