web-dev-qa-db-ja.com

nullリテラルのタイプは何ですか?

D ear all、C#のnullリテラルの型は何でしょうか?

Javaでは、nullリテラル は特殊なnullタイプ です:

名前のない式nullのタイプである特別なnull typeもあります。 null型には名前がないため、null型の変数を宣言したり、null型にキャストしたりすることはできません。 null参照は、null型の式の唯一の可能な値です。 null参照は、常に任意の参照型にキャストできます。

C++ 11には、nullptr(古いバディの推奨バージョンNULL)があり、これは タイプstd::nullptr_t です。

C#についてMSDNを検索しましたが、 仕様 はそれについて何も言っていないようです。

55
Vlad

ECMA C#言語仕様 によると:

9.4.4.6 nullリテラル:

Null-literalの型はnull型です(§11.2.7)。

11.2.7 null型:

Nullリテラル(§9.4.4.6)はnull値に評価されます。これは、オブジェクトまたは配列を指していない参照、または値がないことを示すために使用されます。 null型には、null値である単一の値があります。したがって、型がnull型である式は、null値にのみ評価できます。 null型を明示的に書き込む方法はないため、宣言された型で使用することはできません。さらに、null型は型パラメーターに対して推論された型になることはできません(§25.6.4)。

だからあなたの質問に答えるために、nullはそれ自体のタイプです-nullタイプ。

C#4.0言語仕様 または C#3.0言語仕様 に記載されていないのは奇妙ですが、 C#3.0の概要 に記載されていますが、 ECMA C#言語仕様 および C#2.0言語仕様

55
Johannes Kommer

更新: この質問は2013年7月に私のブログの主題でした。 素晴らしい質問をありがとう!


J.Kommerの答えは正しいです(そして、明らかに仕様の掘り起こしが多かったことを実行するのに適しています)が、少し歴史的な視点を追加したいと思いました。

Madsと私がC#3.0の仕様のさまざまな部分の正確な表現を整理しているときに、「null型」は奇妙であることに気付きました。値が1つしかない「タイプ」です。リフレクションが何も知らない「型」です。これは、名前がなく、GetTypeが決して返さない、ローカル変数またはフィールドなどのタイプとして指定できない「タイプ」です。要するに、それは実際には「型」であり、型システムを「完全」にするためだけに存在するため、すべてのコンパイル時の式には型があります。

C#には既に型のない式があったことを除いて、C#1.0のメソッドグループ、C#2.0の匿名メソッド、C#3.0のラムダはすべて型を持ちません。これらすべてに型がない場合、「null」にも型が必要ないことに気づきました。したがって、C#3.0では不要な「null型」への参照を削除しました。

実装の詳細として、C#1.0から5.0のMicrosoft実装にはすべて、「null型」を表す内部オブジェクトがあります。また、存在しないタイプのラムダ、無名メソッド、メソッドグループを表すオブジェクトもあります。この実装の選択には、多くの長所と短所があります。プロ側では、コンパイラーは任意の式のタイプを要求して、答えを得ることができます。逆に言えば、型分析のバグのせいで実際にはコンパイラーがクラッシュするはずであったため、プログラムのセマンティックな変更が発生するということです。私のお気に入りの例は、C#2.0では不正な式「null ?? null」を使用できることです。バグが原因で、コンパイラは??演算子の誤った使用としてフラグを立てることができず、この式の型が「null型」であると推測し続けます。その後、型アナライザーが型を理解しようとすると、それが他の多くのダウンストリームバグの原因になります。

Roslynでは、おそらくこの戦略を使用しません。むしろ、一部の式に型がないことをコンパイラの実装に焼き付けます。

45
Eric Lippert

ランタイム型がないにもかかわらず、この例が示すように、nullはコンパイル時に型にキャストできます。

実行時に、変数stringAsObjectstringだけでなくobjectも保持していることがわかりますが、変数nullStringおよび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
emrgee