2016-03-09 9 views
16

Spodziewałem się, że to zadziała, ale najwyraźniej sposób, w jaki generuje IL, rzuca NullReferenceException. Dlaczego kompilator nie może wygenerować podobnego kodu dla zapytań?C# 6 null operator warunkowy nie działa dla zapytania LINQ

W przypadku ThisWorks kompilator generuje kod, który powoduje zwarcie reszty wyrażenia, dlaczego nie może zrobić tego samego dla przypadku zapytania LINQ?

class Target 
{ 
    public ChildTarget Child; 
} 

class ChildTarget 
{ 
    public int[] Values; 
} 

IEnumerable<int> ThisWorks(Target target) => 
    target.Child?.Values.Select(x => x); 

IEnumerable<int> ThisDoesNotWork(Target target) => 
    from x in target.Child?.Values select x; 

ThisWorks(new Target()); 
ThisDoesNotWork(new Target()); // this throws NullReferenceException 

dekompilowana wyniki

private static IEnumerable<int> ThisDoesNotWork(Target target) 
{ 
    ChildTarget child = target.Child; 
    IEnumerable<int> values = (child != null) ? child.Values : null; 
    Func<int, int> func; 
    if ((func = Program._func) == null) 
    { 
     func = (Program._func = new Func<int, int>(Program._funcMethod)); 
    } 
    return values.Select(func); 
} 

private static IEnumerable<int> ThisWorks(Target target) 
{ 
    ChildTarget child = target.Child; 
    IEnumerable<int> values; 
    if (child == null) 
    { 
     values = null; 
    } 
    else 
    { 
     IEnumerable<int> values = child.Values; 
     Func<int, int> func; 
     if ((func = Program._func2) == null) 
     { 
      func = (Program._func2= new Func<int, int>(Program._funcMethod2)); 
     } 
     values = values.Select(func); 
    } 
    return values; 
} 
+0

Domyślam się, że kompilator tłumaczy najpierw operator warunkowy o wartości null, zanim przetłumaczy składnię zapytania. Więc twoje zapytanie jest podobne do '(Child == null? Null: Child.Values). Wybierz (x => x)'. Gdyby najpierw przetłumaczył składnię zapytania na składnię metody, zadziałałoby. – juharr

+0

Dlaczego miałbyś się spodziewać, że zachowa się inaczej? W twoim przykładzie parametr 'target.Child? .Values' zwraca wartość' null'. Warunek zerowy ma wpływ tylko na wyrażenie, którego jest częścią. Skutecznie robisz 'null.Select (...)'. –

+0

@JeffMercado Spodziewam się, że 'od x w e? .Wartość wybierz x' to' e? .Wartość.Wybierz (x = x>) '* od * późniejsze wyrażenie * działa * to jest niespodzianka. Jak zaznaczył @Neal, to subtelny bracketing jest winien, a także kolejność transformacji. –

Odpowiedz

9

Odpowiedź jest w specyfikacji języka C#, który mówi

Wyrażenie kwerendy formularza

z x w e wybrać x

przekłada się

(e). Wybierz (x =>x)

zanotować nawiasów wokół e w ostatnim wierszu. Pokazuje to wyraźnie, że wyrażenie warunkowe (w twoim przykładzie) kończy się przed wywołaniem Select, co oznacza, że ​​Select może zostać wywołany z wynikową wartością null.

Dlaczego nie może zrobić tego samego dla Linq? Ponieważ nie jest to sposób, w jaki funkcja została zaprojektowana do działania. Specyfikacja operatorów warunkowych o wartości null nie ma specjalnego przypadku dla zapytań ani odwrotnie.

+0

Planowałem znaleźć specyfikację warunkowego operatora o wartości zerowej, aby uzupełnić to wyjaśnienie, tylko po to, aby stwierdzić, że nie było żadnego. Teraz trzeba będzie poświęcić trochę czasu na przeanalizowanie tego wątku, aby dowiedzieć się, co się stało: https://roslyn.codeplex.com/discussions/540883 –

+0

Potwierdziło to również '(target.Child? .Values). Wybierz (x => x) 'także wyrzuca, zgodnie z oczekiwaniami. –

Powiązane problemy