2013-04-30 22 views
94

Poniższe wyniki kodowe w wykorzystania nieprzypisanych zmiennej lokalnej "numberOfGroups":Dlaczego ten warunek (null ||! TryParse) powoduje "użycie nieprzypisanej zmiennej lokalnej"?

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

Jednak ten kod działa poprawnie (choć ReSharper mówi = 10 jest zbędny):

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

Am Brakuje mi czegoś lub kompilator nie lubi mojego ||?

Zawęziłem to do dynamic, powodując problemy (options była zmienną dynamiczną w powyższym kodzie). Pozostaje pytanie, dlaczego nie mogę tego zrobić?

Ten kod nie kompilacji:

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 
} 

Jednak ten kod robi:

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 
} 

nie zdawałem sobie sprawy dynamic będzie czynnikiem w tej sprawie.

+0

nie sądzę, że jest wystarczająco inteligentny wiedzieć, że nie używasz wartości przekazanej do twojego parametru "out" jako wejścia – Charleh

+0

Gdzie jest błąd? Czy używasz 'numberOfGroups' * po * kodzie, który pokazałeś? Czy możesz zmienić pytanie, aby pokazać krótki, ale kompletny program demonstrujący problem? –

+3

Podany tutaj kod nie demonstruje opisanego zachowania; działa dobrze. Proszę napisać kod, który * faktycznie demonstruje zachowanie, które opisujesz *, które możemy skompilować. Prześlij nam całą teczkę. –

Odpowiedz

72

Jestem prawie pewien, że jest to błąd kompilatora. Ładne znalezisko!

Edycja: to nie jest błąd, jak pokazuje Quartermeister; dynamic może zaimplementować dziwny operator true, który może spowodować, że y nie zostanie zainicjowany.

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

nie widzę powodu, dlaczego tak powinno być nielegalne; jeśli zamienisz dynamicznie na bool, kompiluje się dobrze.

Naprawdę spotykam się jutro z zespołem C#; Wspomnę o tym im. Przepraszamy za błąd!

+6

Po prostu cieszę się, że nie oszalałem :) Odkąd zaktualizowałem swój kod, aby polegać tylko na TryParse, więc jestem ustawiony na teraz. Dzięki za twój wgląd! –

+0

@EricLippert, czy to błąd, czy raczej błąd konstrukcyjny? Ponieważ, jak wskazuje moja odpowiedź, kompilator jest zaprojektowany * nie *, aby rozwiązać dowolne wyrażenie ze zmienną 'dynamic'. Być może przypadek 'bool' musiał być traktowany oddzielnie? – NominSim

+0

@BrandonMartinez: Lub, po prostu zainicjuj lokalny. –

7

From MSDN (Kopalnia nacisk):

The dynamic type enables the operations in which it occurs to bypass compile-time type checking. Instead, these operations are resolved at run time. The dynamic type simplifies access to COM APIs such as the Office Automation APIs, and also to dynamic APIs such as IronPython libraries, and to the HTML Document Object Model (DOM).

Type dynamic behaves like type object in most circumstances. However, operations that contain expressions of type dynamic are not resolved or type checked by the compiler.

Ponieważ kompilator nie typ kontroli lub rozwiązać wszelkie operacje, które zawierają wyrażenia typu dynamicznego, nie może zapewnić, że zmienna będzie przypisana poprzez wykorzystanie TryParse().

+0

Jeśli pierwszy warunek jest spełniony, przypisywane jest 'liczbaGrupy' (w' jeśli true 'blok), jeśli nie, drugi warunek gwarantuje przypisanie (przez' out'). – leppie

+1

To jest ciekawa myśl, * ale * kod kompiluje się dobrze bez 'myString == null' (powołując się tylko na' TryParse'). –

+1

@leppie Chodzi o to, że ponieważ pierwszy warunek (a przez to całe wyrażenie "if") zawiera zmienną "dynamiczną", nie jest rozwiązywany w czasie kompilacji (kompilator nie może zatem wykonać tych założeń). – NominSim

51

Możliwe jest, że zmienna zostanie cofnięta, jeśli wartość dynamicznego wyrażenia jest przeciążona przeciążonym operatorem.

Operator || będzie wywoływał operatora true zdecydować, czy do oceny prawą stronę, a następnie stwierdzenie if będzie wywoływał operatora true do decydowania o ocenę jego ciało. W przypadku normalnego bool, zawsze będą zwracać ten sam wynik, więc dokładnie jeden zostanie oceniony, ale dla operatora zdefiniowanego przez użytkownika nie ma takiej gwarancji!

budynku off Repro Eric Lippert, oto jest krótki i kompletny program, który pokazuje przypadek, w którym żadna ścieżka będzie wykonywana i zmienna miałaby swoją pierwotną wartość:

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(); 
    } 
} 
+0

To jest całkiem schludne, ale nie jestem pewien, czy odnosi się ono do przypadku OP, ponieważ jest wywoływany operator równości; Nie sądzę, aby w takim przypadku można było wywołać niestandardowy operator true. – dlev

+0

@dlev: Wyrażenie równości jest również dynamicznie powiązane i może wiązać się z operatorem zdefiniowanym przez użytkownika, który zwraca coś innego niż 'bool'. – Quartermeister

+0

Ups, good call; zapomniałem 'operator ==' nie musi zwracać 'bool'. – dlev

Powiązane problemy