Kısa devre yapan operatörler yapın || ve && boş verilebilir booleler için var mı? RuntimeBinder bazen öyle düşünüyor


84

Koşullu mantıksal işleçler hakkında || ve &&aynı zamanda kısa devre yapan mantıksal işleçler olarak da bilinen C # Dil Belirtimini okudum . Bana göre bunların nullable booleanlar için var olup olmadığı belirsiz görünüyordu, yani işlenen türü Nullable<bool>(ayrıca yazılmıştır bool?), bu yüzden dinamik olmayan yazımla denedim:

bool a = true;
bool? b = null;
bool? xxxx = b || a;  // compile-time error, || can't be applied to these types

Bu soruyu çözüyor gibiydi (Spesifikasyonu net bir şekilde anlayamadım, ancak Visual C # derleyicisinin uygulanmasının doğru olduğunu varsayarsak, şimdi biliyordum).

Ancak ben dynamicde ciltlemeyi denemek istedim . Ben de bunu denedim:

static class Program
{
  static dynamic A
  {
    get
    {
      Console.WriteLine("'A' evaluated");
      return true;
    }
  }
  static dynamic B
  {
    get
    {
      Console.WriteLine("'B' evaluated");
      return null;
    }
  }

  static void Main()
  {
    dynamic x = A | B;
    Console.WriteLine((object)x);
    dynamic y = A & B;
    Console.WriteLine((object)y);

    dynamic xx = A || B;
    Console.WriteLine((object)xx);
    dynamic yy = A && B;
    Console.WriteLine((object)yy);
  }
}

Şaşırtıcı sonuç, bunun istisnasız çalışmasıdır.

Eh, xve yşaşırtıcı değildir, onların beyanlar hem özellikleri alındığını yol ve elde edilen değerler beklendiği gibi olan xbir trueve ybir null.

Ama için değerlendirme xxarasında A || Bhiçbir bağlayıcı zamanlı istisna kurşun ve sadece mülkiyet Adeğil, okundu B. Bu neden oluyor? BAnlayabileceğiniz gibi "Hello world", alıcıyı çılgın bir nesneye döndürmek için değiştirebiliriz ve xxyine de truebağlama problemleri olmadan değerlendirebiliriz ...

Değerlendirilmesi A && B(için yy) ayrıca bağlanma süresi hatasına yol açmaz. Ve burada elbette her iki mülk de alınır. Çalışma zamanı bağlayıcı buna neden izin veriyor? Gönderilen nesne B"kötü" bir nesneye (a gibi string) değiştirilirse, bir bağlama istisnası oluşur.

Bu doğru davranış mı? (Spesifikasyondan bunu nasıl çıkarabilirsin?)

Eğer denerseniz Bilk işlenen olarak, hem B || Ave B && A(çalışma zamanı bağlayıcı istisna vermek B | Ave B & Aher şeyi olmayan kısa devre operatörleri ile normaldir olarak çalışma cezası |ve &).

(Visual Studio 2013'ün C # derleyicisi ve .NET 4.5.2 çalışma zamanı sürümü ile denendi.)


4
Hiçbir şekilde Nullable<Boolean>dahil olma durumu yoktur , yalnızca kutulu boole'lar olarak değerlendirilir dynamic- ile yaptığınız testin bool?ilgisi yoktur. (Elbette, bu tam bir cevap değil, sadece bir tanesi.)
Jeroen Mostert

3
A || BDeğerlendirmek istediğiniz kalmamasıdır içinde, bir anlamda belirli bir miktarda yapar Bsürece Ayanlıştır o değil ki. Yani gerçekten ifadenin türünü asla bilemezsiniz. A && BVersiyon daha şaşırtıcı olan - Ben spec ne bulabilirsiniz göreceksiniz.
Jon Skeet

2
@JeroenMostert: derleyici tipi eğer karar vermesi, sürece Eh, Aolan boolve değeri Bolan null, daha sonra bir bool && bool?operatör dahil olabilir.
Jon Skeet

4
İlginç bir şekilde, bu bir derleyici veya özellik hatasını ortaya çıkardı. C # 5.0 belirtimi &&, sanki onun &yerine öyleymiş gibi çözümleme hakkında konuşur ve özellikle her iki işlenenin olduğu durumu içerir bool?- ancak daha sonra başvurduğu sonraki bölüm, boş verilebilir durumu işlemez. Bu konuda daha fazla ayrıntıya giren bir tür cevap ekleyebilirim, ancak bu tam olarak açıklamaz.
Jon Skeet

14
Mads'e teknik özellik sorunu hakkında e-posta gönderdim, bunun sadece nasıl okuduğumla ilgili bir sorun olup olmadığını görmek için ...
Jon Skeet

Yanıtlar:


67

Her şeyden önce, dinamik olmayan null yapılabilir bool durumunda spesifikasyonun net olmadığını belirttiğiniz için teşekkür ederiz. Bunu gelecekteki bir sürümde düzelteceğim. Derleyicinin davranışı amaçlanan davranıştır; &&ve ||nullable bool'lar üzerinde çalışmaması gerekir.

Dinamik bağlayıcı bu kısıtlamayı uygulamıyor gibi görünüyor. Bunun yerine, bileşen işlemlerini ayrı ayrı bağlar: &/ |ve ?:. Böylece, ilk işlenen trueveya false(bunlar boolean değerleridir ve bu nedenle ilk işlenen olarak izin verilir ?:) olursa, ancak nullilk işlenen olarak verirseniz (örneğin B && Ayukarıdaki örnekte denerseniz ), bunu yaparsınız. bir çalışma zamanı bağlama istisnası alın.

Düşünürseniz, neden dinamik uyguladığımızı &&ve ||tek bir büyük dinamik işlem yerine bu şekilde uyguladığımızı görebilirsiniz : dinamik işlemler, işlenenleri değerlendirildikten sonra çalışma zamanında bağlanır , böylece bağlama, sonuçların çalışma zamanı türlerine dayanabilir. bu değerlendirmelerden. Ancak böylesine hevesli bir değerlendirme, operatörlerin kısa devre yapma amacını ortadan kaldırır! Bunun yerine, dinamik için oluşturulan kod &&ve ||şekilde ilerler ve parçalar halinde sonları değerlendirme takip:

  • Sol operandı değerlendirin (sonucu diyelim x)
  • Bunu boolörtük bir dönüştürme yoluyla veya trueveya falseoperatörlerine dönüştürmeyi deneyin (mümkünse başarısız olun)
  • xBir ?:operasyonda koşul olarak kullanın
  • Gerçek dalda, xsonuç olarak kullanın
  • Yanlış dalda, şimdi ikinci işleneni değerlendirin (sonucu çağıralım y)
  • Bağlamak için deneyin &veya |çalışma zamanı türüne göre operatörü xve y(yapamaz eğer başarısız)
  • Seçili operatörü uygula

Bu işlenen bazı "yasadışı" kombinasyonlar yoluyla sağlayan bir davranıştır: ?:operatör başarıyla olarak birinci işlenen davranır olmayan null , boolean &veya |bir şekilde operatör başarıyla davranır onu null boolean ve iki onlar katılıyorum kontrol etmek koordinat asla .

Yani o kadar dinamik değil && ve || nullable'lar üzerinde çalışın. Statik duruma kıyasla biraz fazla hoşgörülü bir şekilde uygulanmış olmalarıdır. Bu muhtemelen bir hata olarak görülmelidir, ancak bu büyük bir değişiklik olacağı için asla düzeltmeyeceğiz. Ayrıca kimsenin davranışı sıkılaştırmasına pek yardımcı olmaz.

Umarım bu, ne olduğunu ve neden olduğunu açıklar! Bu ilgi çekici bir alan ve dinamiği hayata geçirdiğimizde verdiğimiz kararların sonuçları karşısında kendimi sık sık şaşkın buluyorum. Bu soru çok lezzetliydi - sorduğun için teşekkürler!

Mads


Bu kısa devre operatörlerinin özel olduğunu görebiliyorum, çünkü dinamik bağlama ile kısa devre yaptığımız durumda ikinci işlenenin türünü bilmemize gerçekten izin verilmiyor. Belki de teknik özellik bundan bahsetmeli? Elbette dynamic, a'nın içindeki her şey kutu içinde olduğundan, bool?hangisi ile HasValue"basit" arasındaki farkı söyleyemeyiz bool.
Jeppe Stig Nielsen

6

Bu doğru davranış mı?

Evet, olduğundan oldukça eminim.

Bunu spesifikasyondan nasıl çıkarabilirsin?

C # Spesifikasyon Sürümü 5.0 Bölüm 7.12, koşullu işleçler &&ve ||bunlarla dinamik bağlamanın nasıl ilişkili olduğu hakkında bilgi içerir. İlgili bölüm:

Bir koşullu mantıksal operatörün bir işleneni derleme zamanı dinamik türüne sahipse, ifade dinamik olarak bağlanır (§7.2.2). Bu durumda, ifadenin derleme zamanı türü dinamiktir ve aşağıda açıklanan çözüm , derleme zamanı türü dinamiğine sahip olan işlenenlerin çalışma zamanı türü kullanılarak çalışma zamanında gerçekleşecektir .

Sanırım sorunuzu yanıtlayan kilit nokta bu. Çalışma zamanında gerçekleşen çözüm nedir? Bölüm 7.12.2, Kullanıcı Tanımlı koşullu mantıksal operatörler şunları açıklamaktadır:

  • X && y işlemi, T. yanlış (x)? x: T. & (x, y), burada T.false (x), T'de yanlış beyan edilen işlecin bir çağrısıdır ve T. & (x, y), seçilen operatörün bir çağrısıdır &
  • X || y, T. doğru (x) olarak değerlendirilir? x: T. | (x, y), burada T. doğru (x), T'de açıklanan true operatörünün bir çağrısıdır ve T. | (x, y), seçilen | operatörünün bir çağrısıdır.

Her iki durumda da, birinci işlenen x, falseveya trueoperatörleri kullanılarak bir bool'a dönüştürülür . Daha sonra uygun mantıksal operatör çağrılır. Bunu akılda tutarak, geri kalan sorularınızı yanıtlamak için yeterli bilgiye sahibiz.

Ancak xx of A için değerlendirme || B, hiçbir bağlama süresi istisnasına yol açmaz ve yalnızca B özelliği okundu, B değil, okundu. Bu neden oluyor?

İçin ||operatör, bunu takip biliyoruz true(A) ? A : |(A, B). Kısa devre yapıyoruz, bu yüzden bağlayıcı bir zaman istisnası almayacağız. Bile Aoldu false, biz olurdu hala çünkü belirtilen çözünürlük adımlarının, istisna bağlayıcı bir çalışma zamanını alamadım. Eğer Abir falsebiz o zaman bunu |başarıyla Bölüm 7.11.4 başına, boş değerler işleyebilir operatörü.

A && B'nin değerlendirilmesi (yy için) ayrıca hiçbir bağlama süresi hatasına yol açmaz. Ve burada elbette her iki mülk de alınır. Çalışma zamanı bağlayıcı buna neden izin veriyor? B'den döndürülen nesne "kötü" bir nesneye (bir dizge gibi) değiştirilirse, bir bağlama istisnası oluşur.

Benzer nedenlerden dolayı bu da işe yarıyor. &&olarak değerlendirilir false(x) ? x : &(x, y). Abaşarılı bir şekilde a'ya dönüştürülebilir bool, bu nedenle orada bir sorun yoktur. Çünkü Bnull, &operatör bir sürer birinden (Bölüm 7.3.7) kaldırıldığında boolalır birine bool?parametreleri ve böylece hiçbir çalışma zamanı istisnası yoktur.

Her iki koşullu işleç için, Bbool (veya boş dinamik) dışında bir şey ise , parametre olarak bool ve bool olmayan bir aşırı yük bulamadığı için çalışma zamanı bağlama başarısız olur. Bununla birlikte, bu yalnızca Aoperatör için ilk koşulu ( trueiçin ||, falseiçin &&) karşılayamazsa olur . Bunun olmasının nedeni, dinamik bağlamanın oldukça tembel olmasıdır. AYanlış olmadığı sürece mantıksal operatörü bağlamayı denemeyecektir ve mantıksal operatörü değerlendirmek için bu yoldan gitmesi gerekir. Bir kez Aoperatör için ilk koşul tatmin edememesi, bu bağlayıcı hariç başarısız olur.

B'yi ilk işlenen olarak denerseniz, her ikisi de B || A ve B && A, çalışma zamanı bağlayıcı istisnası verir.

Umarım şimdiye kadar bunun neden olduğunu zaten biliyorsunuzdur (ya da açıklamakta kötü bir iş yaptım). Bu koşullu işleci çözümlemenin ilk adımı , mantıksal işlemi işlemeden önce ilk işleneni almak Bve bool dönüştürme işleçlerinden birini ( false(B)veya true(B)) kullanmaktır. Elbette B, olma veya nulldeğerine dönüştürülemez ve bu nedenle çalışma zamanı bağlama istisnası gerçekleşir.truefalse


dynamicBağlamanın, derleme zamanı türlerini (ilk alıntınız) değil, örneklerin gerçek türlerini kullanarak çalışma zamanında gerçekleşmesi şaşırtıcı değildir. İkinci alıntınız önemsizdir çünkü buradaki hiçbir tür operator trueve operator false. Bir explicit operatordönen booldaha başka bir şeydir operator trueve false. Spesifikasyonu A && B(benim örneğimde), statik olarak yazılabilen booleanların ve derleme zamanında bağlama ile a && bnerede ave bstatik olarak yazıldıklarına da izin vermeden herhangi bir şekilde okumak zordur . Yine de buna izin verilmiyor. bool? abool? b
Jeppe Stig Nielsen

-1

Null yapılabilir tür Koşullu mantıksal işleçleri tanımlamaz || ve &&. Size aşağıdaki kodu öneririm:

bool a = true;
bool? b = null;

bool? xxxxOR = (b.HasValue == true) ? (b.Value || a) : a;
bool? xxxxAND = (b.HasValue == true) ? (b.Value && a) : false;
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.