2016-04-17 11 views
3

Beim Aufrufen einer Erweiterungsmethode für einen Ausdruck, der einen elvis-Operator enthält (d. H. Den nullsitiven Dereferenzierungsoperator;?.) Wird der resultierende Nullwert nicht wie erwartet an die Erweiterungsmethode übergeben. Im Wesentlichen kann es zu unerwarteten Null-Referenz-Ausnahmen kommen. Hier ist ein Programm, das dies zeigt:Veranlasst der Elvis-Operator (Nullsave-Dereferenzierungsoperator) Null-Referenzausnahmen?

class Program 
{ 
    static void Main(string[] args) 
    { 
     string nil = null; 
     foreach (var c in ((nil?.ToCharArray()).EmptyIfDefault())) { }; // works 
     foreach (var c in (nil?.ToCharArray().EmptyIfDefault())) { }; // nullref 
    } 
} 

public static class Utility 
{ 
    public static char[] EmptyIfDefault(this char[] target) 
    { 
     return target ?? new char[0]; 
    } 
} 

Hat jemand, wenn dieses Verhalten von Entwurf ist? Beachten Sie, dass es keine gibt? zwischen dem ToCharArray() und dem EmptyIfDefault. Wenn es soweit wäre, würde ich das aktuelle Verhalten verstehen. Gerade jetzt scheint es wie ein Fehler. (Was ist die richtige Methode, dies Microsoft zu melden?)

Für andere, die das gleiche Verhalten sehen: die zusätzlichen Klammern scheinen es zu verhindern.

(Übrigens: hier ist die eigentliche EmptyIfNull ich :)

public static IEnumerable<TTarget> EmptyIfNull<TTarget>(this IEnumerable<TTarget> target) 
    { 
     return target ?? Enumerable.Empty<TTarget>(); 
    } 

bearbeiten bin mit einschließen werde ich nur die Antwort unten in meine Frage gegeben:

Es ist verwandt mit ein häufiger Fehler:

var txt = "I am " + 
    age>=18 ? "mature" : "not old" + 
    " enough."; 

Dieses auch als

interpretiert
var txt = "I am " + 
    age >= 18 
     ? "mature" 
     : ("not old" + " enough."); 

Wenn neu geschrieben, das Verhalten ohne Klammern macht Sinn:

foreach(var c in 
    nil == null 
     ? null 
     : nil.ToCharArray().EmptyIfDefault()) { }; // nullref 
+0

'elvis operator' hat noch nie davon gehört, aber ich mag es. – Steve

Antwort

5

Während es zunächst nicht intuitiv ist, ist es auf jeden Fall kein Fehler ist. Sie bekommen eine NullReferenceException weil Sie versuchen, über null iterieren (es ist nicht die Ausnahme zu werfen, während die Ausdrücke Auswertung)

ist bei diesem Beispiel sehen lassen:

var t = nil?.ToCharArray().EmptyIfNull(); 

Die oben wird nicht Anruf EmptyIfNull, weil nilnull ist und die Methodenkette kurzgeschlossen wird, um null zurückzugeben.

IEnumerable<char> t; 
if (nil != null) 
    t = nil.ToCharArray().EmptyIfNull(); 
else 
    t = null; 

Beachten Sie, dass die EmptyIfNullnur ausgeführt, wenn der Anfangszustand geht (das heißt, nil nicht null ist):

Das heißt, wir können die oben schreiben.

Nun, warum beheben Klammern es?

var t = (nil?.ToCharArray()).EmptyIfNull(); 

Dies kann neu geschrieben werden als:

IEnumerable<char> t; 
IEnumerable<char> temp; 
if (nil != null) 
    temp = nil.ToCharArray(); 
else 
    temp = null; 
t = temp.EmptyIfNull(); 

Sehen Sie, dass das Kurzschlussverhalten gilt nur für den inneren Ausdruck - und wir Anruf dann immer EmptyIfNull auf dem Ergebnis.

+1

Ah, so hat es mit dem Operator precedense zu tun. Der "Dot-Operator" (ich nenne ihn einfach so) geht dem "Null-Dereferincing-Operator" voraus. var t = null == null? null: nil.toCharArray(). EmptyIfNull() – realbart