【问题标题】:What is the type of null literal?空文字的类型是什么?
【发布时间】:2012-01-02 12:35:56
【问题描述】:

Dear all,我想知道C#中null字面量的类型是什么?

在 Java 中,null 文字 is of the special null type

还有一个特殊的空类型,表达式null的类型,它没有名字。因为 null 类型没有名字,所以不可能声明一个 null 类型的变量或强制转换为 null 类型。空引用是空类型表达式的唯一可能值。空引用始终可以转换为任何引用类型。

在C++11中,有nullptr(老哥NULL的推荐版本),也就是is of type std::nullptr_t

我在 MSDN 上搜索了有关 C# 的信息,但 specification 似乎对此没有任何说明。

【问题讨论】:

  • 我不会准确地将 null 描述为具有类型。
  • @ChaosPandion:所以,在 C# 中,不是 每个 表达式都有类型吗?
  • @ChaosPandion:嗯,看来你没那么错!看看埃里克的回答。
  • 还有“方法组”和“lambda表达式”类型。

标签: c# .net language-lawyer


【解决方案1】:

根据ECMA C# language specification

9.4.4.6 空字面量:

null-literal 的类型是 null 类型(第 11.2.7 节)。

11.2.7 null 类型:

空文字(第 9.4.4.6 节)计算为空值,使用 表示不指向任何对象或数组的引用,或者 没有值。 null 类型有一个值,即 空值。因此类型为空类型的表达式可以 仅计算为空值。没有办法明确写 null 类型,因此无法在声明的类型中使用它。 此外,null 类型永远不能是为类型推断的类型 参数(第 25.6.4 节)

所以回答你的问题,null 是它自己的类型 - null 类型。

虽然在C# 4.0 language specificationC# 3.0 language specification 中没有提到它,但在overview of C# 3.0ECMA C# language specificationC# 2.0 language specification 中却提到了它,这很奇怪。

【讨论】:

  • 很奇怪,您对 ECMA 标准的引用与 MSDN 中的文件不同:msdn.microsoft.com/en-us/library/ms228593.aspx
  • @Vlad 这很有趣,可能值得看看Eric Lippert 对此有何评论。
  • 我认为,null 具有单独类型的原因与 Java/C++ 相同。但是我想知道为什么这没有进入 MSDN。
  • @Vlad 也许它在 MSDN 上的单独文档中? (CLR / CTS) 而 ECMA 希望将其添加到基础文档中?
  • 好吧,null 文字本身可能是一个特定于语言的实体。但是 CLI 知道 null 和类型,所以它也必须在 CLR 规范中。
【解决方案2】:

更新:This question was the subject of my blog in July 2013.感谢您提出的好问题!


J.Kommer 的回答是正确的(并且对他们进行了明显的大量规范挖掘!)但我想我会添加一点历史观点。

当 Mads 和我整理 C# 3.0 规范各个部分的确切措辞时,我们意识到“空类型”很奇怪。它是一种只有一个值的“类型”。这是反射一无所知的“类型”。它是一种没有名称的“类型”,GetType 永远不会返回,您不能将其指定为局部变量或字段或任何东西的类型。简而言之,它确实是一种“类型”,它的存在只是为了使类型系统“完整”,因此每个编译时表达式都有一个类型。

除了 C# 已经有没有类型的表达式:C# 1.0 中的方法组、C# 2.0 中的匿名方法和 C# 3.0 中的 lambda 都没有类型。如果所有这些东西都可以没有类型,我们意识到“null”也不需要有类型。因此,我们在 C# 3.0 中删除了对无用“空类型”的引用。

作为实现细节,C# 1.0 到 5.0 的 Microsoft 实现都有一个内部对象来表示“空类型”。它们还有代表不存在类型的 lambda、匿名方法和方法组的对象。这种实施选择有许多优点和缺点。在专业方面,编译器可以询问任何表达式的类型并得到答案。另一方面,这意味着有时类型分析中的错误确实应该使编译器崩溃,而不是导致程序中的语义变化。我最喜欢的例子是在 C# 2.0 中可以使用非法表达式 "null ?? null";由于错误,编译器未能将其标记为 ?? 运算符的错误使用,并继续推断此表达式的类型是“空类型”,即使这不是空文字。当类型分析器试图理解类型时,这会导致许多其他下游错误。

在罗斯林,我们可能不会使用这种策略;相反,我们将简单地将某些表达式没有类型的编译器实现。

【讨论】:

  • 我希望 Roslyn 能够询问任何表达式的类型是什么,并为所有这些非类型类型添加类型定义。否则您将如何回答“null 表达式是什么类型?”的问题
  • @Vlad:C# 3.0 的官方立场是“null”没有类型。
  • @Eric null ?? nullC# 4.0 中编译得很好
  • @KFL:既然你从一个合理的论点中推断出一个错误,那么你的一个前提肯定是不正确的。您认为在 OOP 语言中,表达式 x 可分配给 t 类型 T 的变量意味着(1)x 具有类型 T' 和(2)T' 是 T 的子类型,这完全是错误的。你可以找到很多这个语句被伪造的例子,以double y = 2 + 2;开头。表达式是int类型,变量是double类型,int和double之间没有子类型关系。
  • @KFL:在 C# 中,null 根本没有类型;它当然不是任何事物的子类型。同样,赋值兼容性意味着子类型的想法是错误的。这是另一种方式。子类型意味着赋值兼容性。
【解决方案3】:

尽管没有运行时类型,null 可以在编译时强制转换为类型,如本例所示。

在运行时,您会发现变量stringAsObject 拥有一个string,不仅是一个object,而且您找不到变量nullStringnullStringAsObject 的任何类型。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-25
    • 2014-10-24
    • 2020-04-02
    • 1970-01-01
    • 2012-05-04
    相关资源
    最近更新 更多