var / nullの奇妙な動作で切り替える


91

次のコードがあるとします:

string someString = null;
switch (someString)
{
    case string s:
        Console.WriteLine("string s");
        break;
    case var o:
        Console.WriteLine("var o");
        break;
    default:
        Console.WriteLine("default");
        break;
}

switchステートメントが一致するのはなぜcase var oですか?

(事実上)falseと評価されるため、case string s一致しないのは私の理解です。VS CodeのIntelliSense は、それも同様であると教えてくれます。何かご意見は?s == null(null as string) != nullostring


似ている:nullチェック付きのC#7スイッチケース


9
確認しました。私は、この質問を愛し、特に観察とoされるstring( -つまり、ジェネリックで確認Foo(o)どこFoo<T>(T template) => typeof(T).Name) -それは非常に興味深いケースであるstring x振る舞いは異なるよりはvar xさえx(コンパイラによって)入力時string
マルクGravell

7
デフォルトのケースはデッドコードです。そこで警告を出すべきだと信じてください。チェックしています。
JaredPar 2017年

13
C#の設計者varがこのコンテキストで許可することを決定したのは奇妙です。それは確かに、プログラマーを「成功へと導く」とされる言語ではなく、C ++で見つけたようなもののようです。ここでvarは、あいまいで役に立たないものですが、C#の設計では通常、回避しようとしているようです。
Peter Duniho 2017年

1
@PeterDuniho役に立たないとは思いません。へのインバウンド式はswitch発音できない可能性があります-匿名型など。そして、それは曖昧ではありません-コンパイラはタイプを明確に知っています。nullルールが非常に異なるのは、(少なくとも私にとっては)混乱しているだけです!
Marc Gravell

1
@PeterDunihoの面白い事実-C#1.2仕様から明確な割り当ての正式なルールを検索したことがあり、例示の拡張コードにはブロック(現在の場所)に変数宣言が含まていました。2.0で外側に移動しただけで、キャプチャの問題が明らかな場合は再び内側に戻りました。
Marc Gravell

回答:


69

明示的な型にfor switchを使用するパターンマッチングステートメントの内部ではcase、問題の値がその特定の型か派生型かを尋ねています。これは次とまったく同じですis

switch (someString) {
  case string s:
}
if (someString is string) 

値にnullはタイプがないため、上記のいずれの条件も満たしていません。someStringどちらの例でも、静的型は機能しません。

varパターンマッチングのタイプはワイルドカードとして機能し、を含むすべての値に一致しますnull

defaultここでのケースはデッドコードです。case var o任意の値、nullまたはnull以外と一致します。デフォルト以外のケースは常にデフォルトのケースよりも優先defaultされるため、ヒットすることはありません。ILを見ると、放出されていないことがわかります。

一見すると、これが警告なしにコンパイルされるのは奇妙に思えるかもしれません(間違いなく私を追い出しました)。しかし、これは1.0に戻るC#の動作と一致しています。コンパイラdefaultは、ヒットしないことを自明に証明できる場合でも、ケースを許可します。例として以下を考えてみましょう:

bool b = ...;
switch (b) {
  case true: ...
  case false: ...
  default: ...
}

ここdefaultでヒットすることはありません(bool値が1でも0 でもない場合でも)。しかし、C#は警告なしにこれを1.0以降許可しています。ここでは、パターンマッチングがこの動作と一致しています。


4
しかし、本当の問題は、コンパイラーが実際varには型stringではない(正直なところ、型が何であるかははっきり分からない)ときに「型」であることを「示す」ことです
shmuelie

@shmuelie var例のタイプはと計算されますstring
JaredPar 2017年

5
@JaredParここでの洞察をありがとう; 個人的には、以前は警告を出さなかった場合でも、より多くの警告を出すことをサポートしますが、言語チームの制約を理解しています。「レガシーストイックモード」(選択的)と対比して、「すべてについてモード」(おそらくデフォルトでオン)を検討したことがありますか?たぶんcsc /stiffUpperLip
マークグラベル

3
@MarcGravell新しいウェーブレットを導入するのをより簡単に、互換性を損なうことなくすることを意図したワーニングウェーブと呼ばれる機能があります。本質的にすべてのコンパイラリリースは新しいwaveであり、/ wave:1、/ wave:2、/ wave:allを介して警告をオプトインできます。
JaredPar 2017年

4
@JonathanDickinsonそれはあなたがそれが示しているとあなたが思うことを示しているとは思わない。ちょうど示すことnull有効でありstring、参照、および任意のstring参照(を含むがnull)暗黙的に(参照が保存)キャストすることができobject、参照、および任意のobjectある参照がnullまだされて、他のタイプに正常にアップキャスト(明示的に)することができnull。コンパイラ型システムに関しては、実際には同じではありません。
Marc Gravell

22

私は複数のTwitterコメントをここにまとめています-これは実際に私にとって新しいものであり、jaredparがより包括的な答えでジャンプすることを望んでいます。私が理解している短いバージョン:

case string s:

if(someString is string) { s = (string)someString; ...またはとして解釈されif((s = (someString as string)) != null) { ... }ます-どちらもnullテストを含みます-あなたのケースでは失敗します; 逆に:

case var o:

コンパイラーは単純にそのまま解決oします。表面上は似ているという事実にもかかわらず、コンパイラーが型を提供するだけで、テストはありません。stringo = (string)someString; ...null

最後に:

default:

上記のケースはすべてをキャッチするため、ここには到達できません。これは、到達不能コード警告を発しなかったという点で、コンパイラーのバグである可能性があります。

これは非常に微妙でニュアンスがあり、紛らわしいことに同意します。しかし、明らかにcase var oシナリオにはnull伝播(o?.Length ?? 0など)の使用があります。これがとの間で非常に異なる動作をするのは奇妙であることに同意しますが、これはコンパイラが現在行っていることです。var ostring s


14

これは、静的(コンパイル時)タイプではなく、動的(ランタイム)タイプにcase <Type>一致するためです。動的タイプがないため、と照合できません。ただのフォールバックです。nullstringvar

(短い回答が好きなので投稿します。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.