Bu (null ||! TryParse) koşulu neden “atanmamış yerel değişken kullanımı” ile sonuçlanıyor?


98

Aşağıdaki kod , atanmamış yerel değişken "numberOfGroups" kullanımıyla sonuçlanır :

int numberOfGroups;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
    numberOfGroups = 10;
}

Bununla birlikte, bu kod iyi çalışıyor ( ReSharper bunun = 10gereksiz olduğunu söylüyor ):

int numberOfGroups = 10;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
    numberOfGroups = 10;
}

Bir şey mi kaçırıyorum yoksa derleyici benden hoşlanmıyor ||mu?

Bunu dynamicsorunlara neden olacak şekilde daralttım ( optionsyukarıdaki kodumda dinamik bir değişkendi). Soru hala duruyor, neden bunu yapamıyorum ?

Bu kod değil derleme:

internal class Program
{
    #region Static Methods

    private static void Main(string[] args)
    {
        dynamic myString = args[0];

        int myInt;
        if(myString == null || !int.TryParse(myString, out myInt))
        {
            myInt = 10;
        }

        Console.WriteLine(myInt);
    }

    #endregion
}

Ancak bu kod şunları yapar :

internal class Program
{
    #region Static Methods

    private static void Main(string[] args)
    {
        var myString = args[0]; // var would be string

        int myInt;
        if(myString == null || !int.TryParse(myString, out myInt))
        {
            myInt = 10;
        }

        Console.WriteLine(myInt);
    }

    #endregion
}

Bunda dynamicbir faktör olacağını bilmiyordum .


outGirdi olarak parametrenize iletilen değeri kullanmadığınızı bilecek kadar akıllı olduğunu düşünmeyin
Charleh

3
Burada verilen kod, açıklanan davranışı göstermez; gayet iyi çalışıyor. Lütfen tanımladığınız davranışı gösteren ve kendimizi derleyebileceğimizi gösteren bir kod gönderin . Bize dosyanın tamamını ver.
Eric Lippert

8
Ah, şimdi ilginç bir şeyimiz var!
Eric Lippert

1
Derleyicinin bununla karıştırılması çok da şaşırtıcı değil. Dinamik arama sitesi için yardımcı kod, büyük olasılıkla outparametrenin atanmasını garanti etmeyen bazı kontrol akışına sahiptir . Derleyicinin sorunu önlemek için hangi yardımcı kodu üretmesi gerektiğini veya bu mümkün olup olmadığını düşünmek kesinlikle ilginçtir.
CodesInChaos

1
İlk bakışta bu kesinlikle bir böcek gibi görünüyor.
Eric Lippert

Yanıtlar:


73

Bunun bir derleyici hatası olduğundan oldukça eminim. Güzel bul!

Düzenleme: Quartermeister'ın gösterdiği gibi bu bir hata değildir; dynamic , hiçbir zaman başlatılmamasına trueneden olabilecek garip bir işleç uygulayabilir y.

İşte minimal bir repro:

class Program
{
    static bool M(out int x) 
    { 
        x = 123; 
        return true; 
    }
    static int N(dynamic d)
    {
        int y;
        if(d || M(out y))
            y = 10;
        return y; 
    }
}

Bunun yasa dışı olması için hiçbir neden göremiyorum; dynamic'i bool ile değiştirirseniz, gayet iyi derler.

Aslında yarın C # ekibiyle buluşuyorum; Onlara bundan bahsedeceğim. Hata için özür dileriz!


6
Delirmediğime sevindim :) O zamandan beri kodumu yalnızca TryParse'a güvenecek şekilde güncelledim, bu yüzden şimdilik hazırım. Anlayışınız için teşekkürler!
Brandon Martinez

4
@NominSim: Çalışma zamanı analizinin başarısız olduğunu varsayalım: daha sonra yerel okunmadan önce bir istisna atılır. Çalışma zamanı analizinin başarılı olduğunu varsayalım: o zaman çalışma zamanında ya d doğrudur ve y ayarlıdır ya da d yanlıştır ve M, y'yi ayarlar. Her iki durumda da y ayarlıdır. Analizin çalışma zamanına kadar ertelenmesi gerçeği hiçbir şeyi değiştirmez.
Eric Lippert

2
Merak eden biri olursa: Sadece kontrol ettim ve Mono derleyici doğru anladı. imgur.com/g47oquT
Dan Tao

17
Derleyici davranışının aslında doğru olduğunu düşünüyorum, çünkü değeri daşırı yüklenmiş bir trueoperatörü olan bir tipte olabilir . Hiçbir şubenin alınmadığı bir örnekle bir cevap gönderdim.
Quartermeister

2
@Quartermeister, bu durumda Mono derleyicisi yanlış
anlıyor

52

Dinamik ifadenin değeri aşırı yüklenmiş bir trueoperatörü olan bir türdeyse, değişkenin atanmamış olması mümkündür .

||Operatör çağıracağı truesağ tarafını değerlendirmek için karar vermek operatörü ve sonra ififadesi çağıracağı trueonun vücudunu değerlendirmek için karar vermek operatörü. Normalde boolbunlar her zaman aynı sonucu verir ve bu nedenle tam olarak biri değerlendirilir, ancak kullanıcı tanımlı bir operatör için böyle bir garanti yoktur!

Eric Lippert'in reprosundan yola çıkarak, burada hiçbir yolun yürütülemeyeceği ve değişkenin başlangıç ​​değerine sahip olacağı bir durumu gösteren kısa ve eksiksiz bir program var:

using System;

class Program
{
    static bool M(out int x)
    {
        x = 123;
        return true;
    }

    static int N(dynamic d)
    {
        int y = 3;
        if (d || M(out y))
            y = 10;
        return y;
    }

    static void Main(string[] args)
    {
        var result = N(new EvilBool());
        // Prints 3!
        Console.WriteLine(result);
    }
}

class EvilBool
{
    private bool value;

    public static bool operator true(EvilBool b)
    {
        // Return true the first time this is called
        // and false the second time
        b.value = !b.value;
        return b.value;
    }

    public static bool operator false(EvilBool b)
    {
        throw new NotImplementedException();
    }
}

8
Burada iyi iş çıkardın. Bunu C # test ve tasarım ekiplerine ilettim; Yarın görüştüğümde herhangi bir yorumu olup olmadığını göreceğim.
Eric Lippert

3
Bu benim için çok garip. Neden diki kez değerlendirilmeli? (Gösterdiğiniz gibi açıkça olduğunu tartışmıyorum .) Değerlendirilen sonucun true(ilk operatör çağrısından, neden tarafından ||) ififadeye "iletilmesini" beklerdim. Örneğin, oraya bir işlev çağrısı koyarsanız kesinlikle böyle olur.
Dan Tao

3
@DanTao: İfade d, beklediğiniz gibi yalnızca bir kez değerlendirilir. Bu var truebir kez, iki kez çağrılan ediliyor operatör ||ve bir kez if.
Quartermeister

2
@DanTao: Onları ayrı ifadelere koyarsak daha net olabilir var cond = d || M(out y); if (cond) { ... }. İlk önce dbir EvilBoolnesne referansı almayı değerlendiriyoruz . Bunu değerlendirmek için ||, önce EvilBool.truebu referansla çağırıyoruz . Bu true döndürür, bu yüzden kısa devre yaparız ve çağırmayız Mve sonra referansı atarız cond. Ardından ififadeye geçiyoruz. ifDeyim arayarak kendi durumunu değerlendirir EvilBool.true.
Quartermeister

2
Şimdi bu gerçekten harika. Doğru veya yanlış operatör olduğu hakkında hiçbir fikrim yoktu.
IllidanS4,

7

MSDN'den (vurgu benim):

Dinamik tür, derleme zamanı tür denetimini atlamak için meydana geldiği işlemleri etkinleştirir . Bunun yerine, bu işlemler çalışma zamanında çözülür . Dinamik tür, Office Otomasyon API'leri gibi COM API'lerine ve ayrıca IronPython kitaplıkları gibi dinamik API'lere ve HTML Belge Nesne Modeli'ne (DOM) erişimi basitleştirir.

Type dynamic, çoğu durumda tür nesnesi gibi davranır. Ancak, dinamik türünde ifadeler içeren işlemler çözümlenmez veya derleyici tarafından tür denetlenmez.

Derleyici, dinamik türünde ifadeler içeren herhangi bir işlem denetimi yazmadığı veya çözümlemediği için, değişkenin kullanımıyla atanacağını garanti edemez TryParse().


İlk koşul karşılanırsa, numberGroupsatanır ( if trueblokta), değilse, ikinci koşul atamayı (aracılığıyla out) garanti eder .
leppie

1
Bu ilginç bir düşünce, ancak kod myString == null(sadece TryParse) olmadan iyi bir şekilde derleniyor .
Brandon Martinez

1
@leppie Buradaki nokta, ilk koşul (aslında tüm ififadenin) bir dynamicdeğişken içerdiğinden , derleme zamanında çözülmemesidir (bu nedenle derleyici bu varsayımları yapamaz).
NominSim

@NominSim: Demek istediğini anlıyorum :) +1 Derleyiciden bir fedakarlık olabilir (C # kurallarını çiğnemek), ancak diğer öneriler bir hata anlamına geliyor gibi görünüyor. Eric'in pasajı bunun bir fedakarlık değil, hata olduğunu gösteriyor.
leppie

@NominSim Bu doğru olamaz; belirli derleyici işlevlerinin ertelenmiş olması, hepsinin olduğu anlamına gelmez. Biraz farklı koşullar altında, dinamik bir ifadenin varlığına rağmen derleyicinin kesin atama analizini sorunsuz bir şekilde yaptığını gösteren pek çok kanıt vardır.
dlev
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.