var / null garip davranışla değiştir


91

Aşağıdaki kod verildiğinde:

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 ifadesi neden eşleşiyor case var o?

Benim anlayışıma göre case string s, s == null(etkili) (null as string) != nullyanlış olarak değerlendirildiğinde eşleşmiyor . VS Code üzerindeki IntelliSense bana bunun oda bir olduğunu söylüyor string. Düşüncesi olan var mı?


Benzer: Boş denetimlerle C # 7 anahtar durumu


9
Onaylanmış. Bu soruyu, özellikleo de string(jeneriklerle doğrulanan - yani Foo(o)nerede Foo<T>(T template) => typeof(T).Name)string xvar xxstring
Marc Gravell

7
Varsayılan durum, ölü koddur. İnanın orada bir uyarı yapmalıyız. Kontrol etme.
JaredPar

13
C # tasarımcılarının varbu bağlamda izin vermeye karar vermesi bana garip geliyor . Bu kesinlikle C ++ 'da bulacağım türden bir şey gibi görünüyor, programcıyı "başarı çukuruna" götürdüğü iddia edilen bir dilde değil. Burada, varC # tasarımının genellikle kaçınmaya çalıştığı hem belirsiz hem de yararsız şeyler var.
Peter Duniho

1
@PeterDuniho Yararsız demezdim; için gelen ifade switchtelaffuz edilemez olabilir - anonim tipler, vb; ve belirsiz değildir - derleyici türü açıkça bilir; nullkuralların çok farklı olması kafa karıştırıcı (en azından benim için) !
Marc Gravell

1
@PeterDuniho eğlenceli gerçeği - bir zamanlar C # 1.2 belirtiminden kesin atama resmi kurallarına bakmıştık ve açıklayıcı genişletme kodu bloğun içinde değişken bildirimine sahipti (şimdi olduğu yerde); sadece 2.0'da dışarıya taşındı , ardından yakalama sorunu bariz olduğunda tekrar içeri girdi.
Marc Gravell

Yanıtlar:


69

Bir örüntü eşleştirme switchifadesinin içinde case, açık bir tür için a kullanan , söz konusu değerin bu belirli türden mi yoksa türetilmiş türden mi olduğunu soruyor. Tam eşdeğeriis

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

Değerin nullbir türü yoktur ve bu nedenle yukarıdaki koşulların hiçbirini karşılamaz. Statik tip someStringher iki örnekte de devreye girmez.

varDesende eşleştirme olsa tip vahşi kart olarak görür ve dahil olmak üzere herhangi bir değer maç olacak null.

Buradaki defaultdava ölü koddur. case var oHerhangi bir değer, null adlı veya olmayan null adlı eşleşir. Temerrütlü olmayan bir durum her zaman varsayılan olandan defaultdaha fazla kazanır, dolayısıyla asla vurulmayacaktır. IL'ye bakarsanız, yayınlanmadığını bile görürsünüz.

Bir bakışta, bunun herhangi bir uyarı olmadan derlenmesi garip görünebilir (kesinlikle beni attı). Ancak bu, 1.0'a geri dönen C # davranışı ile eşleşiyor. Derleyici, defaultasla vurulmayacağını önemsiz bir şekilde kanıtlayabilse bile durumlara izin verir . Örnek olarak şunları düşünün:

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

Burada defaultasla vurulmayacak ( bool1 veya 0 olmayan bir değere sahip olsa bile ). Yine de C # buna 1.0'dan beri uyarı yapmadan izin verdi. Örüntü eşleme burada bu davranışla aynı çizgide.


4
Asıl mesele olsa derleyici "gösterileri" olduğunu vartürde olması stringonu (tip kuşkusuz ne olması gerektiğini emin dürüstçe değil) gerçekten olmadığı zaman
shmuelie

@shmuelie türünün varörnekte olduğu hesaplanmıştır string.
JaredPar

5
@JaredPar buradaki bilgiler için teşekkürler; Şahsen, daha önce yapmadığında bile daha fazla uyarı gönderilmesini desteklerdim, ancak dil ekibinin kısıtlamalarını anlıyorum. Hiç "her şey hakkında sızlanma" modunu (muhtemelen varsayılan olarak açıktır), "eski stoacı modu" (seçmeli) düşündünüz mü? belkicsc /stiffUpperLip
Marc Gravell

3
@MarcGravell, daha kolay, daha az uyumsuz, yeni uyarılar sunmayı amaçlayan uyarı dalgaları adı verilen bir özelliğimiz var. Esasen her derleyici sürümü yeni bir dalgadır ve uyarıları / wave: 1, / wave: 2, / wave: all yoluyla seçebilirsiniz.
JaredPar

4
@JonathanDickinson Bunun ne gösterdiğini düşündüğünüzü gösterdiğini sanmıyorum. Bu sadece a'nın nullgeçerli bir stringreferans olduğunu ve herhangi bir stringreferansın (dahil null) örtük olarak bir objectreferansa çevrilebileceğini (referansı koruyarak) ve başka herhangi bir türe başarılı bir şekilde objectyukarıdan null(açık) gönderilebileceğini gösterir null. Derleyici türü sistem açısından gerçekten aynı şey değil.
Marc Gravell

22

Burada birden fazla twitter yorumunu bir araya getiriyorum - bu aslında benim için yeni ve umuyorum ki jaredpar daha kapsamlı bir cevapla devreye girecek, ama; Anladığım kadarıyla kısa versiyon:

case string s:

Sizin durumunuzda başarısız olan if(someString is string) { s = (string)someString; ...veya if((s = (someString as string)) != null) { ... }- herhangi biri bir nulltesti içeren - olarak yorumlanır ; tersine:

case var o:

burada derleyici basitçe oolduğu gibi çözülür - yüzeyde benzer görünmesine rağmen , sadece türü sağlayan derleyici ile test yok .stringo = (string)someString; ...null

en sonunda:

default:

buraya ulaşılamaz , çünkü yukarıdaki dava her şeyi yakalar. Bu, erişilemez bir kod uyarısı vermediği için bir derleyici hatası olabilir.

Bunun çok ince ve incelikli ve kafa karıştırıcı olduğuna katılıyorum . Ama görünüşe göre case var osenaryo boş yayılımlı kullanımlara sahip ( o?.Length ?? 0vb.). Bunun ve arasında çok farklı çalışmasının tuhaf olduğunu kabul ediyorum , ancak derleyicinin şu anda yaptığı şey bu.var ostring s


14

Bunun nedeni , statik (derleme zamanı) türdeki değil dinamik (çalışma zamanı) türdeki case <Type>eşleşmelerdir . dinamik bir türü olmadığı için eşleşemez . sadece geri dönüş.nullstringvar

(Kısa cevapları sevdiğim için gönderiyorum.)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.