简体   繁体   中英

C# static classes and the is operator

After refactoring some code recently, which involved some class renames, some of my code broke in a surprising way. The cause was a failing "is" operator test, that I was very surprised wasn't a compiler error or warning.

This complete program shows the situation:

static class ExtensionMethods {}

class Program {

    static void Main() {
        Test("Test");
    }

    public static bool Test(object obj)
    {
        return obj is ExtensionMethods;
    }
}

I would have expected "obj is ExtensionMethods" to raise a warning of some sort, given that ExtensionMethods is a static class.

The compiler will issue a warning for the "is" operator when object under test can never be of the provided type, ((string)obj) is System.Uri for example.

Am I forgetting a scenario in which this would actually be a meaningful test?

I was very surprised wasn't a compiler error or warning.

It should have been. It was an oversight.

There were a number of bugs like that involving static classes. If I recall correctly, there was even some bizarre scenario that Vladimir Reshetnikov found where it was possible to make type inference infer a static type as a bound on a type parameter.

Apparently this one, which I have seen before, never got fixed. Apologies for the oversight.

Am I forgetting a scenario in which this would actually be a meaningful test?

No.

From the C# 3.0 specification, section 10.1.1.3:

A static class may not include a class-base specification (§10.1.4) and cannot explicitly specify a base class or a list of implemented interfaces. A static class implicitly inherits from type object.

Therefore the compiler apparently does not raise the warning because it does not know that the is will always return false. (A static class "is" an object , and therefore the compiler does not know that an object "is" or "is" not a static class at compile time.) Realistically it likely does know, or at least could find out , but apparently it doesn't specialize that case and check.

I took a crack at this, and although I cannot find this in the MSDN reference, it appears as though the is operatror depends on instantiating a type to be able to check against. Since static classes cannot be instatiated (because static classes are objects created on the program stack at compile time)...

For example, if you do the below you get the following error: "Cannot declare a variable of static type"

ExtensionMethods ex;

Nor if you do the below you get the following error: "Cannot create an instance of static class"

ExtensionMethods ex2 = new ExtensionMethods();

To demonstrate this issue, here is a complete program showing that the is operator.

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;
        }
    }
}

Here is the following output:

True
False
False

The is object is able to check against an instantiable class with the first statement--thus leading me to believe the is operator depends on it. I hope somebody can confirm this?

Courtesy of NominSim::

From the C# 3.0 specification, section 10.1.1.3:

A static class may not include a class-base specification (§10.1.4) and cannot explicitly specify a base class or a list of implemented interfaces. A static class implicitly inherits from type object.

Eric Lippert's answer from 2013 explains that this was a bug in the Visual C# 5.0 compiler (and some earlier versions too). The problem was there with the as operator too, for example object bad = obj as ExtensionMethods; .

In C# 6.0 (from 2015), and later, you do get a compile-time error (not just a warning):

error CS7023: The second operand of an 'is' or 'as' operator may not be static type 'Xxxx'

However, this is only true if you specify feature Strict, see another thread for details on how to do that .

According to the C# Language Specification:

The is operator is used to dynamically check if the run-time type of an object is compatible with a given type. The result of the operation E is T, where E is an expression and T is a type, is a boolean value indicating whether E can successfully be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion.

and

A static class may not include a class-base specification (§10.1.4) and cannot explicitly specify a base class or a list of implemented interfaces. A static class implicitly inherits from type object.

Since it inherits from System.Object implicitly, it makes sense that the compiler is not issuing a warning.

Verification:

var staticBaseType = typeof(B).BaseType;

You will get a System.Object as the base type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM