What is the type of null literal?

前端 未结 3 1344
礼貌的吻别
礼貌的吻别 2020-11-30 00:14

Dear all, I wonder what is the type of null literal in C#?

In Java, the null literal is of the special null type:

Th

相关标签:
3条回答
  • 2020-11-30 00:58

    According to the ECMA C# language specification:

    9.4.4.6 The null literal:

    The type of a null-literal is the null type (§11.2.7).

    11.2.7 The null type:

    The null literal (§9.4.4.6) evaluates to the null value, which is used to denote a reference not pointing at any object or array, or the absence of a value. The null type has a single value, which is the null value. Hence an expression whose type is the null type can evaluate only to the null value. There is no way to explicitly write the null type and, therefore, no way to use it in a declared type. Moreover, the null type can never be the type inferred for a type parameter (§25.6.4)

    So to answer your question, null is it's own type - the null type.

    Although it's odd how it's not mentioned in the C# 4.0 language specification or the C# 3.0 language specification but is mentioned in the overview of C# 3.0, the ECMA C# language specification and the C# 2.0 language specification.

    0 讨论(0)
  • 2020-11-30 01:00

    UPDATE: This question was the subject of my blog in July 2013. Thanks for the great question!


    J.Kommer's answer is correct (and good on them for doing what was evidently a lot of spec digging!) but I thought I'd add a little historical perspective.

    When Mads and I were sorting out the exact wording of various parts of the specification for C# 3.0 we realized that the "null type" was bizarre. It is a "type" with only one value. It is a "type" that Reflection knows nothing about. It is a "type" that doesn't have a name, that GetType never returns, that you can't specify as the type of a local variable or field or anything. In short, it really is a "type" that is there only to make the type system "complete", so that every compile-time expression has a type.

    Except that C# already had expressions that had no type: method groups in C# 1.0, anonymous methods in C# 2.0 and lambdas in C# 3.0 all have no type. If all those things can have no type, we realized that "null" need not have a type either. Therefore we removed references to the useless "null type" in C# 3.0.

    As an implementation detail, the Microsoft implementations of C# 1.0 through 5.0 all do have an internal object to represent the "null type". They also have objects to represent the non-existing types of lambdas, anonymous methods and method groups. This implementation choice has a number of pros and cons. On the pro side, the compiler can ask for the type of any expression and get an answer. On the con side, it means that sometimes bugs in the type analysis that really ought to have crashed the compiler instead cause semantic changes in programs. My favourite example of that is that it is possible in C# 2.0 to use the illegal expression "null ?? null"; due to a bug the compiler fails to flag it as an erroneous usage of the ?? operator, and goes on to infer that the type of this expression is "the null type", even though that is not a null literal. That then goes on to cause many other downstream bugs as the type analyzer tries to make sense of the type.

    In Roslyn we will probably not use this strategy; rather, we'll simply bake into the compiler implementation that some expressions have no type.

    0 讨论(0)
  • 2020-11-30 01:02

    Despite of having no runtime type, null can be cast to a type at compile time, as this example shows.

    At runtime, you can find that variable stringAsObject holds a string, not only an object, but you cannot find any type for variables nullString and nullStringAsObject.

    public enum Answer { Object, String, Int32, FileInfo };
    private Answer GetAnswer(int i) { return Answer.Int32; }
    private Answer GetAnswer(string s) { return Answer.String; }
    private Answer GetAnswer(object o) { return Answer.Object; }
    
    [TestMethod]
    public void MusingAboutNullAtRuntimeVsCompileTime()
    {
        string nullString = null;
        object nullStringAsObject = (string)null;
        object stringAsObject = "a string";
    
        // resolved at runtime
        Expect.Throws(typeof(ArgumentNullException), () => Type.GetTypeHandle(nullString));
        Expect.Throws(typeof(ArgumentNullException), () => Type.GetTypeHandle(nullStringAsObject));
        Assert.AreEqual(typeof(string), Type.GetTypeFromHandle(Type.GetTypeHandle(stringAsObject)));
        Assert.AreEqual(typeof(string), stringAsObject.GetType());
    
        // resolved at compile time
        Assert.AreEqual(Answer.String, this.GetAnswer(nullString));
        Assert.AreEqual(Answer.Object, this.GetAnswer(nullStringAsObject));
        Assert.AreEqual(Answer.Object, this.GetAnswer(stringAsObject));
        Assert.AreEqual(Answer.Object, this.GetAnswer((object)null));
        Assert.AreEqual(Answer.String, this.GetAnswer((string)null));
        Assert.AreEqual(Answer.String, this.GetAnswer(null));
    }
    
    // Uncommenting the following method overload
    // makes the last statement in the test case ambiguous to the compiler
    // private Answer GetAnswer(FileInfo f) { return Answer.FileInfo; }
    
    0 讨论(0)
提交回复
热议问题