私はここで何が起こっているのかについて合理的な推測をすることができますが、それはすべて少し複雑です:)これには、ドラフト仕様で説明されているnull状態とnullトラッキングが含まれます。基本的に、戻りたいポイントで、式の状態が「nullでない」ではなく「nullかもしれない」場合、コンパイラーは警告を出します。
この答えは、「ここに結論があります」というよりはむしろいくぶん物語的な形になっています...私はそれがそのようにより有用であることを望みます。
フィールドを取り除くことで例を少し単純化し、次の2つのシグネチャのいずれかを持つメソッドを検討します。
public static string M(string? text)
public static string M(string text)
以下の実装では、各メソッドに異なる番号を付けているため、特定の例を明確に参照できます。また、すべての実装を同じプログラムに含めることができます。
以下で説明するそれぞれのケースでは、さまざまなことを行いますが、最終的には戻ろうとするtext
ため、それはnull状態であるtext
ことが重要です。
無条件返品
最初に、直接返してみましょう:
public static string M1(string? text) => text; // Warning
public static string M2(string text) => text; // No warning
これまでのところ、とても簡単です。メソッドの開始時のパラメータのnull可能状態は、それがtypeの場合は「多分null」であり、typeのstring?
場合は「not null」ですstring
。
単純な条件付きリターン
次に、if
ステートメント条件自体の中でnullをチェックします。(私は条件演算子を使用しますが、これは同じ効果があると思いますが、質問に対してより忠実であり続けたいと思いました。)
public static string M3(string? text)
{
if (text is null)
{
return "";
}
else
{
return text; // No warning
}
}
public static string M4(string text)
{
if (text is null)
{
return "";
}
else
{
return text; // No warning
}
}
すばらしいのでif
、条件自体がnull かどうかをチェックするステートメント内のように見えます。ステートメントの各ブランチ内の変数の状態はif
異なる場合があります。else
ブロック内では、状態は両方のコードで「nullではない」です。したがって、特にM3では、状態が「nullかもしれない」から「nullでない」に変わります。
ローカル変数による条件付き戻り
次に、その条件をローカル変数に巻き上げてみましょう。
public static string M5(string? text)
{
bool isNull = text is null;
if (isNull)
{
return "";
}
else
{
return text; // Warning
}
}
public static string M6(string text)
{
bool isNull = text is null;
if (isNull)
{
return "";
}
else
{
return text; // Warning
}
}
M5とM6の両方が警告を発行します。したがって、M5での「多分null」から「not null」への状態変化のプラスの効果が得られないだけでなく(M3で行ったように)、M6で反対の効果が得られます。nullではない」から「nullかもしれない」まで。本当にびっくりしました。
つまり、次のことを学んだようです。
- 「ローカル変数の計算方法」に関するロジックは、状態情報の伝達には使用されません。詳細は後ほど。
- null比較を導入すると、以前はnullではないと考えていたものが結局nullになる可能性があることをコンパイラに警告できます。
無視された比較の後の無条件の戻り
無条件に戻る前に比較を導入して、これらの箇条書きの2番目を見てみましょう。(したがって、比較の結果は完全に無視しています。):
public static string M7(string? text)
{
bool ignored = text is null;
return text; // Warning
}
public static string M8(string text)
{
bool ignored = text is null;
return text; // Warning
}
M8がM2と同等であるように見えることに注意してください-どちらも無条件で返される非nullパラメータを持っています-しかし、nullとの比較を導入すると、状態が「非null」から「多分null」に変わります。text
条件の前に逆参照を試みることにより、これのさらなる証拠を得ることができます。
public static string M9(string text)
{
int length1 = text.Length; // No warning
bool ignored = text is null;
int length2 = text.Length; // Warning
return text; // No warning
}
return
ステートメントに警告が表示されていないことに注意してください。実行後の状態text.Length
は「nullではない」です(その式を正常に実行した場合、nullにできないため)。そのため、text
パラメーターは、そのタイプのために「nullではない」として始まり、null比較のために「nullかもしれない」になり、その後再び「nullでない」になりtext2.Length
ます。
どのような比較が状態に影響しますか?
それが比較ですtext is null
...同様の比較はどのような効果をもたらしますか?次に、4つのメソッドを示します。すべてnull不可の文字列パラメーターで始まります。
public static string M10(string text)
{
bool ignored = text == null;
return text; // Warning
}
public static string M11(string text)
{
bool ignored = text is object;
return text; // No warning
}
public static string M12(string text)
{
bool ignored = text is { };
return text; // No warning
}
public static string M13(string text)
{
bool ignored = text != null;
return text; // Warning
}
したがって、x is object
がの代替として推奨されている場合でもx != null
、同じ効果はありません。null との比較(is
、==
またはのいずれかを使用!=
)のみが、状態を「null以外」から「おそらくnull」に変更します。
なぜ状態を上げると効果があるのですか?
前の最初の箇条書きに戻って、M5とM6がローカル変数の原因となった条件を考慮しないのはなぜですか?これは、他の人を驚かせるように見えるほど私を驚かせません。この種のロジックをコンパイラと仕様に組み込むことは多くの作業であり、メリットはほとんどありません。何かをインライン化することで影響が出るnull可能性とは何の関係もない別の例を次に示します。
public static int X1()
{
if (true)
{
return 1;
}
}
public static int X2()
{
bool alwaysTrue = true;
if (alwaysTrue)
{
return 1;
}
// Error: not all code paths return a value
}
にもかかわらず、我々はそれが知っているalwaysTrue
、常にtrueになります、それは後に、コードを作る仕様で要件満たしていないif
私たちが必要とするものである到達不可能な文を、。
明確な割り当てに関する別の例を次に示します。
public static void X3()
{
string x;
bool condition = DateTime.UtcNow.Year == 2020;
if (condition)
{
x = "It's 2020.";
}
if (!condition)
{
x = "It's not 2020.";
}
// Error: x is not definitely assigned
Console.WriteLine(x);
}
コードがこれらのステートメント本体の1つに正確に入ることがわかっている場合でもif
、仕様にはそれを解決するものは何もありません。静的分析ツールはそうすることができるかもしれませんが、言語仕様にそれを入れようとすることは悪い考えです、IMO-静的分析ツールがすべての種類のヒューリスティックを持っていることは問題ありません。言語仕様。