przełącznik z var / null dziwne zachowanie

Podano następujący kod:

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

Dlaczego polecenie switch pasuje do case var o?

Rozumiem, że case string s nie pasuje, Kiedy s == null, ponieważ (skutecznie) (null as string) != null ocenia NA false. IntelliSense NA VS Code mówi mi, że o jest string również. Jakieś pomysły?


Podobne do: C # 7 switch case with null checks

Author: Servy, 2017-06-14

3 answers

Wewnątrz dopasowującej się do wzorca instrukcji switch używanie case dla jawnego typu jest pytaniem, czy dana wartość jest tego konkretnego typu, Czy typu pochodnego. Jest dokładnym odpowiednikiem is

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

Wartość null nie ma typu i dlatego nie spełnia żadnego z powyższych warunków. Typ statyczny someString nie wchodzi w grę w żadnym z przykładów.

Typ var w pattern matching działa jak dzika karta i dopasuje dowolną wartość, w tym null.

Sprawa tutaj to martwy kod. case var o dopasuje dowolną wartość, null lub non-null. Nie-domyślny przypadek zawsze wygrywa z domyślnym, dlatego default nigdy nie zostanie trafiony. Jeśli spojrzysz na IL zobaczysz, że nawet nie jest emitowane.

Na pierwszy rzut oka może się wydawać dziwne, że to kompiluje się bez żadnego ostrzeżenia(zdecydowanie mnie wyrzuciło). Ale to jest dopasowanie do zachowania C#, które wraca do 1.0. Kompilator pozwala default na przypadki nawet wtedy, gdy może trywialnie udowodnić, że będzie nigdy nie daj się trafić. Rozważmy jako przykład:

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

Tutaj default nigdy nie zostanie trafiony (nawet dla bool, które mają wartość, która nie jest 1 lub 0). Jednak C# zezwala na to od 1.0 bez ostrzeżenia. Dopasowywanie wzorców jest po prostu zgodne z tym zachowaniem tutaj.

 68
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-13 22:35:10

Zestawiam tutaj wiele komentarzy na Twitterze - to dla mnie nowość i mam nadzieję, że jaredpar wskoczy z bardziej wyczerpującą odpowiedzią, ale krótka wersja, Jak to Rozumiem: {]}

case string s:

Jest interpretowane jako if(someString is string) { s = (string)someString; ... lub if((s = (someString as string)) != null) { ... } - z których każda wymaga testu null - który w Twoim przypadku nie jest udany; odwrotnie:

case var o:

Gdzie kompilator rozwiązuje o jako string jest po prostu o = (string)someString; ... - no null test, pomimo faktu, że wygląda podobnie na surface, tylko z kompilatorem podającym Typ.

Wreszcie:

default:

Tutaj nie można dotrzeć , ponieważ powyższy przypadek łapie wszystko. Może to być błąd kompilatora, ponieważ nie emitował ostrzeżenia o nieosiągalnym kodzie.

Zgadzam się, że to jest Bardzo subtelne i niuansowe, i mylące. Ale najwyraźniej scenariusz case var o ma zastosowania z propagacją null (o?.Length ?? 0 itd.). Zgadzam się, że to dziwne, że to działa tak Bardzo inaczej między var o i string s, Ale To właśnie robi obecnie kompilator.

 22
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-13 22:32:07

To dlatego, że case <Type> pasuje do typudynamic (run-time), a nie do typu statycznego (compile-time). null nie ma typu dynamicznego, więc nie może dopasować się do string. var jest tylko alternatywą.

(postowanie, bo lubię krótkie odpowiedzi.)

 14
Author: Mehrdad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-14 00:38:23