2010-12-22 5 views
1

Gibt es einen pedantischen, auf der Enumerator-Theorie basierenden Grund, warum die Any() -Methode einen ArgumentNullException für einen Null-Enumerable auslöst?Enumerable.Any <T> (diese IEnumerable <T> Quelle) sollte Null aufzählbar auch behandeln, sicher?

ich IEnumerable, wo immer möglich Sammlungen von Daten um passieren - aber in den meisten Fällen dann habe ich diese Art von Code:

public class Foo 
{ 
    public IEnumerable<IBar> Bars { get; set; } 
} 

////---- 

public static void UseAFoo(Foo foo) 
{ 
    //if Bars has something in it, then do something: 
    if(foo.Bars != null && foo.Bars.Any()) 
    { 
    //blah 
    } 
} 

Oder

if((foo.Bars ?? Enumerable.Empty<IBar>()).Any()) 
{ 
    //ugh! 
} 

oder ändern Sie die Eigenschaft Erklärung so :

public class Foo 
{ 
    private IEnumerable<IBar> _bars; 
    public IEnumerable<IBar> Bars 
    { 
    get { return _bars ?? Enumerable.Empty<IBar>(); } 
    set { _bars = value; } 
    } 
} 

Oder die 'effiziente' Art und Weise:

get { return _bars; } 
set { _bars = value ?? Enumerable.Empty<IBar>(); } 

In jedem Fall - es ist böse. Okay, ich kann meine eigene Erweiterungsmethode machen (was ich habe) - aber die Benennung ist ein bisschen schwierig, weil der beste Name schon vergeben ist! Ich bin für NotNullAndAny() gegangen, aber ich mag es nicht.

Any() (und es ist delegate-driven Bruder) sollte meiner Meinung nach false zurückgeben, wenn die Enumerable null ist - es sollte keine ArgumentNullException werfen.

Aber dann wie ich am Anfang sage - gibt es hier einen versteckten Vertrag, den ich vermisse? Oder ist es nur Pedanterie;)

UPDATE

Ich hatte das Gefühl, dass der Konsens sein würde, dass alle Linq Erweiterungen ArgumentNullException werfen sollen, auch den Fall von Any() - und um ehrlich zu sein, während ich das Gefühl, dass der Fall noch für Any() gemacht werden kann, sind die Argumente für nicht überzeugend genug, um die grundlegende Tatsache zu überwinden, dass, wenn ein Aufzählungszeichen null ist, keine allgemeine Operation zufriedenstellend abgeschlossen werden kann und daher eine Ausnahme ausgelöst werden muss.

Also werde ich mit einer zusätzlichen Extension-Methode NotNullAndAny() gehen, um meinen Code zu bereinigen, wo Nullen vernünftigerweise möglich sind (und kein Fehler). In Fällen, in denen ein Null-Enumerable nicht erlaubt ist, werfe ich bereits ArgumentNullException. In einigen Fällen ist es jedoch praktischer, den Verbrauchern meines Codes zu erlauben, eine Eigenschaft unset zu lassen, während meine Klassen nicht zwangsweise manuell implementierte Eigenschaften oder Standardkonstruktoren erfordern, um sicherzustellen, dass diese Eigenschaften niemals eine null erhalten. Wenn das meine Wahl ist, dann übergebe ich meinen eigenen Code, um sowohl null als auch leer zu prüfen.

Einzige Sache ist jetzt, welche Antwort zu markieren als die Antwort! :)

+2

Vielleicht ist dieser Beitrag von Eric Lippert interessant für Sie: [Null ist nicht leer] (http://blogs.msdn.com/ericlippert/archive/2009/05/14/null-is-not-empty .aspx) –

+0

Guter Beitrag - Danke für den Link. Ich verstehe, was er dort sagt - aber für mich scheint es nicht so zu sein, dass es mit der "Any" -Methode gewaschen wird. Ich hatte noch kein Szenario, in dem ich ein Null-Enumerable behandeln würde, das sich von einem leeren unterscheidet, bei dem die 'Any'-Methode mir Probleme bereiten würde, indem ich Nullen behandle. –

+0

es ist eine Erweiterungsmethode - Sie können Ihre eigene Version implementieren, und importieren Sie sie gegenüber der Linq. Aber jetzt schreibst du einen Code, der hinter den Kulissen "mehr Magie" macht (Nullen verstecken), und er wird jemandem, der Linq gewohnt ist, nicht sofort vertraut sein. –

Antwort

2

Ich kann nicht beantworten, ob es einen vernünftigen theoretischen Grund dafür gibt - aber für mich haben wir es mit zwei verschiedenen Konzepten zu tun. Ich werde die Sprache missbrauchen und über Listen sprechen, anstatt über Enums, aber dieselben Argumente sollten gelten: Es gibt einen großen Unterschied zwischen einer Liste, die keine Einträge enthält, und einer Liste, die gar nicht vorhanden ist.


Und persönlich, da Ihre aufgeführten Code Optionen, würde ich mit einem gehen Sie tat es nicht Liste (wenn es sinnvoll ist, eine einstellbare enumerable zu haben, und die Klasse wirklich kann mit jedem enumerable arbeiten):

public class Foo 
{ 
    private IEnumerable<IBar> _bars = Enumerable.Empty<IBar>();; 
    public IEnumerable<IBar> Bars 
    { 
    get { return _bars; } 
    set { 
     if(value==null) throw new ArgumentNullException("value"); 
     _bars = value; 
    } 
    } 
} 

dh lassen Sie sich nicht NULL-Werte in Ihr Design kriechen, wenn Sie mit ihnen umgehen wollen nicht

+0

Ja das ist wahr - die meisten meiner Code hat Null Überprüfung von Parametern usw. überall und so kann ich sehen, das Argument für mich muss es tun - oder in der Tat mit beiden Nullen und leert mich selbst. Ich möchte nicht unbedingt das oben für alle meine Typen tun müssen, da es bedeutet manuell Eigenschaften zu implementieren oder einen Standardkonstruktor manuell zu implementieren. Ich möchte, dass Benutzer eine Instanz dieser Klassen erhalten und die Eigenschaften, die sie festlegen möchten, ausfüllen.+1 obwohl - ich kann sehen, dass der Konsens hier ist, dass Nullen immer noch ein Sonderfall sind, auch für Any() –

+0

@Andras - Ja, ich denke im Allgemeinen, was wir versuchen zu vermitteln ist, dass * Sie * null behandeln wollen und leere Enumerables als gleichwertig, während manche Menschen sie anders behandeln wollen oder müssen. –

2

Ich denke, es sollte false nicht zurückgeben, wenn der Parameter null ist.

Mit diesem einen wirft eine Ausnahme für einen null Parameter entspricht mit dem allgemeinen Verhalten der anderen BCL Klassen.

Es gibt false zurück, wenn die sequenceempty ist.

Es gibt einen Unterschied zwischen einem null Wert und einem Parameterwert als (was eine Sequenz ohne Elemente darin ist).

+0

Ja, aber wenn ein Enumerable null ist, kann per Definition auch nichts darin enthalten sein - daher sollte Any() false sein - keine Ausnahme. –

+1

Das versuche ich zu erklären. Eine Variable mit dem Wert 'null' verweist nicht auf ein Objekt im Speicher. Wenn Sie kein Objekt haben, mit dem Sie Mitglieder verwenden können, können Sie buchstäblich nichts tun. Auf der anderen Seite ist es immer noch ein gültiger Wert, eine 'Sequenz' /' Sammlung' ohne Elemente zu haben. Sie können die Mitglieder trotzdem verwenden, weil sie existieren. – decyclone

+0

mmm - das ist ein fairer Punkt. Ich kann sehen, dass jede andere Erweiterungsmethode immer eine 'ArgumentNullException' aus genau dem Grund werfen muss, die Sie sagen. Nur ein bisschen pedantisch scheint es auch bei Any() zu sein - da es nur die Existenz von mehr als null Elementen bestätigt. Ich weiß, Sie können argumentieren, dass es diese Wahrheit nicht auf einer Nullreferenz bestimmen kann - aber manchmal sollte der gesunde Menschenverstand anstelle von Schwarz-Weiß-Regeln angewendet werden? –

1

Null in der Praxis ist oft ein falscher Zustand und wird als solche von der großen Mehrheit gehandelt APIs - die API für linq folgt diesem . Vielleicht sollten Sie sich fragen, warum Sie überhaupt null Enumerables haben, und sich davor schützen. Ein zweiter Ansatz wäre, eigene Erweiterungsmethoden zu schreiben, die Ihnen das gewünschte Verhalten geben. Übermäßige Null-Überprüfung (im Code statt nur Parameter-Überprüfung) ist in der Regel ein Code-Geruch.

Verwandte Themen