Roslyn kodu derleyemedi


95

Projemi VS2013'ten VS2015'e taşıdıktan sonra proje artık oluşturulmuyor. Aşağıdaki LINQ deyiminde bir derleme hatası oluşur:

static void Main(string[] args)
{
    decimal a, b;
    IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
    var result = (from v in array
                  where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
                  orderby decimal.Parse(v)
                  select v).ToArray();
}

Derleyici bir hata döndürür:

Hata CS0165 Atanmamış yerel değişken 'b' kullanımı

Bu soruna ne sebep olur? Bir derleyici ayarıyla düzeltmek mümkün mü?


11
@BinaryWorrier: Neden? Yalnızca bbir outparametre aracılığıyla atadıktan sonra kullanır .
Jon Skeet

1
VS 2015 belgeleri, " Bağımsız değişkenler olarak iletilen değişkenlerin geçirilmeden önce başlatılması gerekmese de, çağrılan yöntemin, yöntem dönmeden önce bir değer ataması gerekir" diyor. bu yüzden bu bir hataya benziyor, evet, o tryParse tarafından başlatılacağı garantidir.
Rup

3
Hatadan bağımsız olarak, bu kod, outargümanlar hakkında kötü olan her şeyi örnekledi . Bu, TryParseboş bir değer (veya eşdeğeri) döndürür mü?
Konrad Rudolph

1
@KonradRudolph çok daha iyi where (a = decimal.TryParse(v)).HasValue && (b = decimal.TryParse(v)).HasValue && a <= bgörünüyor
Rawling

2
Sadece not etmek gerekirse, bunu basitleştirebilirsiniz decimal a, b; var q = decimal.TryParse((dynamic)"10", out a) && decimal.TryParse("15", out b) && a <= b;. Bunu artıran bir Roslyn hatası açtım .
Rawling

Yanıtlar:


112

Bu soruna ne sebep olur?

Bana bir derleyici hatası gibi görünüyor. En azından yaptı. Her ne kadar decimal.TryParse(v, out a)ve decimal.TryParse(v, out b)ifadeler dinamik değerlendirilir, ben beklenen derleyici hala anlamak için o ulaştığında a <= b, hem ave bkesinlikle atanır. Dinamik yazmada karşılaşabileceğiniz tuhaflıklara rağmen, yalnızca her a <= biki TryParsearamayı da değerlendirdikten sonra değerlendirmeyi bekliyorum .

Ancak, operatör ve zor dönüşüm yoluyla, bir ifadeye sahip tamamen mümkün olduğunu çıkıyor A && B && Cki değerlendirir Ave Cancak Beğer sen kurnaz yeterli -. Neal Gafter'ın ustaca örneği için Roslyn hata raporuna bakın .

Bunu yapmak dynamicdaha da zor - işlenenler dinamik olduğunda dahil olan anlambilimin açıklanması daha zordur, çünkü aşırı yük çözümlemesini gerçekleştirmek için hangi türlerin dahil olduğunu bulmak için işlenenleri değerlendirmeniz gerekir, bu da sezgiye aykırı olabilir. Ancak yine Neal, derleyici hatasının gerekli olduğunu gösteren bir örnek buldu ... bu bir hata değil, bir hata düzeltmesi . Bunu kanıtladığı için Neal'a büyük miktarda övgü

Derleyici ayarları aracılığıyla düzeltmek mümkün mü?

Hayır, ancak hatayı engelleyen alternatifler var.

İlk olarak, dinamik olmasını engelleyebilirsiniz - eğer sadece dizeleri kullanacağınızı biliyorsanız, o zaman aralık değişkenini kullanabilir IEnumerable<string> veyav bir tür string(ie from string v in array) verebilirsiniz . Bu benim tercih ettiğim seçenek olurdu.

Eğer varsa gerçekten o dinamik tutmak için gereken, sadece vermek bbaşlangıç için bir değer:

decimal a, b = 0m;

Bunun herhangi bir zararı olmayacak - aslında dinamik değerlendirmenizin çılgınca bir şey yapmayacağını biliyoruz, bu yüzden bonu kullanmadan önce bir değer atayarak başlangıç ​​değerini geçersiz kılacaksınız.

Ek olarak, parantez eklemek de işe yarıyor gibi görünüyor:

where decimal.TryParse(v, out a) && (decimal.TryParse("15", out b) && a <= b)

Bu, çeşitli aşırı yük çözümleme parçalarının tetiklendiği noktayı değiştirir ve derleyiciyi mutlu eder.

Hala bir sorun var - &&operatörle kesin atamayla ilgili spesifikasyon kurallarının, yalnızca &&operatör iki boolişlenenle "normal" uygulamasında kullanıldığında geçerli olduklarını belirtmek için açıklığa kavuşturulması gerekir . Bunun bir sonraki ECMA standardı için düzeltildiğinden emin olmaya çalışacağım.


Evet! IEnumerable<string>Parantez uygulamak veya eklemek benim için çalıştı. Artık derleyici hatasız derler.
ramil89

1
kullanmak decimal a, b = 0m;hatayı kaldırabilir, ancak çıkış değeri henüz hesaplanmadığı için a <= bher zaman kullanılır 0m.
Paw Baltzersen

12
@PawBaltzersen: Seni böyle düşündüren nedir? Her zaman olacaktır karşılaştırma önce atanabilir - bu derleyici nedense (temelde bir hata,) için, bunu ispat edemezse oluşu.
Jon Skeet

1
Yan etkisi olmayan bir ayrıştırma yöntemine sahip olmak yani. decimal? TryParseDecimal(string txt)çözüm de olabilir
zahir

1
Tembel bir başlatma olup olmadığını merak ediyorum; "Birincisi doğruysa, ikincisini değerlendirmeme gerek yok, bu da atanamayabilir" diye düşünür b; Bunun geçersiz bir mantık olduğunu biliyorum ama parantezlerin neden
düzeltdiğini


16

Hata raporunda çok okula gittiğim için, bunu kendim açıklamaya çalışacağım.


Imagine T, ve ile başlayarak ve boolarasında değişen örtük bir tür olan kullanıcı tanımlı bir türdür . Derleyicinin bildiği kadarıyla, ilk argüman o türe göre değerlendirilebilir, bu yüzden kötümser olması gerekir.falsetruefalsedynamic&&

O zaman kodun derlenmesine izin verirse, bu olabilir:

  • Dinamik bağlayıcı birinciyi değerlendirdiğinde, &&şunları yapar:
    • İlk argümanı değerlendirin
    • Bu bir T- örtük olarak atıyor bool.
    • Oh, bu falseyüzden ikinci argümanı değerlendirmemize gerek yok.
    • Değerlendirmenin sonucunu &&ilk bağımsız değişken olarak yapın. (Hayır, falsenedense hayır .)
  • Dinamik bağlayıcı ikinciyi değerlendirdiğinde, &&şunları yapar:
    • İlk argümanı değerlendirin.
    • Bu bir T- örtük olarak atıyor bool.
    • Oh, trueöyleyse ikinci argümanı değerlendirin.
    • ... Kahretsin, batanmamış.

Spesifik terimlerle, kısaca, sadece bir değişkenin "kesinlikle atanmış" veya "kesinlikle atanmamış" olup olmadığını değil, aynı zamanda " falseifadeden sonra kesinlikle atanmış " veya "kesinlikle trueifadeden sonra atanır ".

Bunlar, &&ve ||(ve !ve ??ve ?:) ile uğraşırken , derleyicinin değişkenlerin karmaşık bir boole ifadesinin belirli dallarına atanıp atanamayacağını inceleyebilmesi için vardır.

Ancak, bunlar yalnızca ifadelerin türleri boole kalırken çalışır . İfadenin bir parçası dynamic(veya boole olmayan statik bir tür) olduğunda, artık ifadenin trueveya olduğunu söyleyemeyiz false- bir dahaki sefere boolhangi dalı alacağımıza karar vermek için kullandığımızda, fikrini değiştirmiş olabilir.


Güncelleme: Bu artık çözüldü ve belgelendi :

Dinamik ifadeler için önceki derleyiciler tarafından uygulanan kesin atama kuralları, kesinlikle atanmamış değişkenlerin okunmasına neden olabilecek bazı kod durumlarına izin veriyordu. Bununla ilgili bir rapor için https://github.com/dotnet/roslyn/issues/4509 adresine bakın .

...

Bu olasılık nedeniyle, val'in başlangıç ​​değeri yoksa derleyici bu programın derlenmesine izin vermemelidir. Derleyicinin önceki sürümleri (VS2015'ten önce), val'nin başlangıç ​​değeri olmasa bile bu programın derlenmesine izin veriyordu. Roslyn şimdi, muhtemelen başlatılmamış bir değişkeni okuma girişimini teşhis ediyor.


1
VS2013'ü diğer makinemde kullanarak, bunu kullanarak atanmamış belleği gerçekten okumayı başardım. Çok heyecan verici değil :(
Rawling

Başlatılmamış değişkenleri basit temsilci ile okuyabilirsiniz. Sahip outolan bir yönteme ulaşan bir temsilci oluşturun ref. Bunu mutlu bir şekilde yapacak ve değeri değiştirmeden değişkenler atayacaktır.
IllidanS4, Monica'yı

Merak ettiğim için, o pasajı C # v4 ile test ettim. Sadece meraktan, gerçi - nasıl derleyici operatörü kullanmaya karar vermez false/ trueörtülü cast operatörü aksine? Yerel olarak, implicit operator boolilk bağımsız değişkeni çağırır, sonra ikinci işleneni çağırır operator false, ilk işleneni çağırır , ardından tekrarimplicit operator bool ilk işleneni çağırır . Bu bana mantıklı gelmiyor, ilk işlenen esasen bir boolean'a indirgenmeli, değil mi?
Rob

@Rob Bu dynamiczincirleme &&durum mu? Temelde gittiğini gördüm (1) ilk argümanı değerlendir (2) kısa devre yapıp yapamayacağımı görmek için örtük atımı kullan (3) yapamam, bu yüzden ikinci argümanı değerlendir (4) şimdi her iki türü de biliyorum, ben en iyisinin &&, kısa devre yapıp yapamayacağımı görmek için ilk argümandaki kullanıcı tanımlı &(5) çağrı operatörü falseolduğunu görebilirim (6) Yapabilirim (çünkü falseve implicit boolkatılmıyorum), yani sonuç ilk argüman ... ve sonra bir sonraki &&, (7) (tekrar) kısa devre yapıp yapamayacağımı görmek için örtük atım kullanın.
Rawling

@ IllidanS4 Kulağa ilginç geliyor ama nasıl yapılacağını bulamadım. Bana bir pasaj verir misin?
Rawling

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.