Ist es unvernünftig zu erwarten, dass Any () * not * eine Nullreferenzausnahme auslöst?

41

Wenn Sie eine Erweiterungsmethode erstellen können Sie, natürlich, nennen es auf null.Aber, im Gegensatz zu einer Instanz Methodenaufruf, es auf null Aufruf nicht haben eine werfen NullReferenceException-> müssen Sie prüfen , und es manuell zu werfen.

Für die Implementierung der Linq-Erweiterungsmethode hat Any()Microsoft entschieden, dass ein ArgumentNullException( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ) ausgelöst werden soll .

Es nervt mich zu schreiben if( myCollection != null && myCollection.Any() )

Bin ich falsch, als Client dieses Codes zu erwarten, dass zB zurückkehren ((int[])null).Any()sollte false?

dies verlängertdass
quelle
41
In C # 6 können Sie die Überprüfung vereinfachen, um (myCollection? .Any () == true)
17 von 26
5
Auch in F #, die wirklich nulls nicht nur mit anderen .NET - Sprachen für die Interoperabilität verwenden, null |> Seq.isEmptyführt System.ArgumentNullException: Value cannot be null. Die Erwartung scheint zu sein, dass Sie keine undefinierten Werte für etwas übergeben, von dem erwartet wird, dass es existiert. Es ist also immer noch eine Ausnahme, wenn Sie eine Null haben. Wenn es um die Initialisierung geht, würde ich mit einer leeren Sequenz anstelle von a beginnen null.
Aaron M. Eshbach
21
Aus diesem Grund sollten Sie nullbeim Umgang mit Sammlungen nicht zurückkehren , sondern in diesen Fällen eine leere Sammlung verwenden.
Bakuriu
31
Nun, warum ist es überhaupt null? Sie möchten nicht, dass null als leer gezählt wird, weil es nicht leer ist, es ist etwas völlig anderes. Vielleicht möchten Sie, dass es in Ihrem Fall als leer gezählt wird, aber wenn Sie so etwas in die Sprache aufnehmen, kommt es zu Fehlern.
user253751
22
Ich sollte darauf hinweisen, dass jede einzelne LINQ-Methode ein Null-Argument auslöst. Anyist nur konsequent.
Sebastian Redl

Antworten:

157

Ich habe eine Tüte mit fünf Kartoffeln drin. Befinden sich .Any()Kartoffeln in der Tüte?

" Ja ", sagst du.<= true

Ich nehme alle Kartoffeln heraus und esse sie. Befinden sich .Any()Kartoffeln in der Tüte?

" Nein ", sagst du.<= false

Ich verbrenne die Tüte komplett im Feuer. Sind .Any()jetzt Kartoffeln in der Tüte?

" Es gibt keine Tasche ."<= ArgumentNullException

Dan Wilson
quelle
3
Aber leer, es sind keine Kartoffeln in der Tüte? Falsch impliziert Falsch ... (Ich bin nicht anderer Meinung, nur eine zweite Perspektive).
D. Ben Knoble
6
Praktischerweise gibt Ihnen jemand eine geschlossene Kiste aus einer unbekannten Quelle. Enthält der Beutel in der Schachtel Kartoffeln? Ich interessiere mich nur für 2 Szenarien - A) in der Schachtel befindet sich eine Tüte mit Kartoffeln oder B) in der Schachtel befinden sich keine Kartoffeln und / oder keine Tüte. Semantisch gesehen gibt es einen Unterschied zwischen null und leer, aber in 99% der Fälle ist mir das egal.
Dave Thieben
6
Du hast fünf rohe Kartoffeln gegessen? Sofort einen Arzt aufsuchen.
Oly
3
@Oly Dan hat sie im Feuer gekocht, bevor er gegessen und das Feuer benutzt hat, um den Beutel zu verbrennen.
Ismael Miguel
3
@ D.BenKnoble: Ohne den Kofferraum meines Autos gesehen zu haben, sind Sie bereit zu bezeugen, dass sich keine Leichen im Kofferraum meines Autos befinden? Nein? Was ist der Unterschied zwischen dem Überprüfen des Kofferraums und dem Nichtfinden von Gegenständen oder dem Nichtüberprüfen des Kofferraums? Können Sie jetzt nicht einfach aussagen, da Sie in beiden Fällen genau die gleiche Anzahl von Körpern gesehen hätten? ... das ist der Unterschied. Sie können nicht für etwas bürgen, wenn Sie es überhaupt nicht bestätigen können. Sie nehmen an, dass die beiden gleichwertig sind, aber das ist keine Selbstverständlichkeit. In einigen Fällen kann es einen wichtigen Unterschied zwischen den beiden geben.
Flater
52

Zunächst scheint es, dass der Quellcode nicht werfen ArgumentNullExceptionwird NullReferenceException.

In vielen Fällen wissen Sie jedoch bereits, dass Ihre Sammlung nicht null ist, da dieser Code nur von Code aufgerufen wird, der weiß, dass die Sammlung bereits vorhanden ist, sodass Sie den Null-Check dort nicht sehr oft eingeben müssen. Aber wenn Sie nicht wissen, dass es existiert, ist es sinnvoll, dies zu überprüfen, bevor Sie es aufrufen Any().

Bin ich falsch, als Client dieses Codes zu erwarten, dass zB zurückkehren ((int[])null).Any()sollte false?

Ja. Die Frage Any()lautet: "Enthält diese Sammlung Elemente?" Wenn diese Sammlung nicht existiert, ist die Frage selbst unsinnig; es kann nichts enthalten oder nicht enthalten, weil es nicht existiert.

Mason Wheeler
quelle
18
@ ChrisWohlert: Ihre Intuition fasziniert mich. Denken Sie auch, dass eine nicht existierende Person semantisch eine leere Person ist, deren Name die leere Zeichenfolge ist und deren Alter Null ist und so weiter? Und denken Sie, dass eine Menge, die enthält, einer Menge nullentspricht, die die leere Menge enthält (und umgekehrt)?
Ruakh
17
@ ChrisWohlert: Offensichtlich kann eine Menge die leere Menge enthalten; aber Sie scheinen das zu glauben { null }und { {} }sollten gleichwertig sein. Das finde ich faszinierend; Ich kann überhaupt nichts damit anfangen.
Ruakh
9
Sollte .Addauf nullirgendeine Weise eine Sammlung auch für uns erstellt werden?
Matthew
13
@ChrisWohlert "A ist ein leerer Satz. B ist ein Satz mit einer Wassermelone. Ist A leer? Ja. Ist B leer? Nein. Ist C leer? Uhhh ..."
user253751
16
Pedantischer Punkt: In der Mengenlehre ist eine nicht existierende Menge keine leere Menge. Die leere Menge existiert, und jede nicht existierende Menge existiert nicht (und ist in der Tat überhaupt nichts, da sie nicht existiert).
James_pic
22

Null bedeutet fehlende Informationen, keine Elemente.

Möglicherweise sollten Sie Null allgemein vermeiden. Verwenden Sie beispielsweise eine der integrierten leeren Enumerables, um eine Auflistung ohne Elemente anstelle von Null darzustellen.

Wenn Sie unter bestimmten Umständen null zurückgeben, können Sie dies ändern, um die leere Auflistung zurückzugeben. (Andernfalls ist es bedauerlich, wenn Sie feststellen, dass Nullen von Bibliotheksmethoden zurückgegeben werden (nicht von Ihren), und ich würde sie zur Normalisierung einpacken.)

Siehe auch https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty

Erik Eidt
quelle
1
Das nullheißt nicht, dass keine Elemente eine sinnvolle Konvention sind. Denken Sie nur an native OLESTRINGS, wo das eigentlich heißt .
Deduplizierer
1
@ Deduplicator, sprechen Sie über die Möglichkeit der Objektverknüpfung und -einbettung von Microsoft? Wenn Sie sich erinnern, dass OLE aus den 90ern stammt! Derzeit bieten viele moderne Sprachen (C #, Java, Swift) Möglichkeiten, die Verwendung von Null zu eliminieren / zu beseitigen.
Erik Eidt
2
@ErikEidt Das machen sie zum Teil, weil nulles nicht "fehlende Informationen" bedeutet. nullhat keine einzige sinnvolle Interpretation. Stattdessen geht es darum, wie Sie damit umgehen. nullwird manchmal für "fehlende Informationen", aber auch für "keine Elemente" und auch für "ein Fehler ist aufgetreten" oder "nicht initialisiert" oder "widersprüchliche Informationen" oder eine beliebige, ad-hoc-Sache verwendet.
Derek Elkins
-1. Dies ist eine Entscheidung des Entwicklers, nicht abstrakter Theorien. nullwird absolut häufig verwendet, um "keine Daten" zu bedeuten. Manchmal macht es Sinn. Andere Male nicht.
jpmc26
13

Abgesehen von der nullbedingten Syntax gibt es eine andere Technik, um dieses Problem zu beheben: Lassen Sie Ihre Variable niemals übrig null.

Stellen Sie sich eine Funktion vor, die eine Auflistung als Parameter akzeptiert. Wenn für die Zwecke der Funktion nullund leer gleichwertig sind, können Sie sicherstellen, dass es nullzu Beginn nie enthält :

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

Sie können dasselbe tun, wenn Sie Sammlungen von einer anderen Methode abrufen:

var words = GetWords() ?? Enumerable.Empty<string>();

(Beachten Sie, dass in Fällen , in denen Sie die Kontrolle über eine Funktion haben , wie GetWordsund nullentsprechen die leere Sammlung, es vorzuziehen ist , zurückzukehren nur die leere Sammlung an erster Stelle.)

Jetzt können Sie jede gewünschte Operation für die Sammlung ausführen. Dies ist besonders hilfreich, wenn Sie viele Vorgänge ausführen müssen, bei denen die Auflistung fehlschlagen würde null, und in Fällen, in denen Sie das gleiche Ergebnis erhalten, indem Sie eine leere Aufzählung durchlaufen oder abfragen, können Sie die ifBedingungen vollständig entfernen.

jpmc26
quelle
12

Bin ich falsch, als Client dieses Codes zu erwarten, dass zB ((int []) null) .Any () false zurückgeben sollte?

Ja, einfach, weil Sie in C # arbeiten und dieses Verhalten gut definiert und dokumentiert ist.

Wenn Sie eine eigene Bibliothek erstellen oder eine andere Sprache mit einer anderen Ausnahmekultur verwenden, ist es vernünftiger, mit false zu rechnen.

Persönlich denke ich, dass return false ein sicherer Ansatz ist, der Ihr Programm robuster macht, aber zumindest umstritten ist.

Telastyn
quelle
15
Diese 'Robustheit' ist oft eine Illusion - wenn der Code, der das Array generiert, plötzlich aufgrund eines Fehlers oder eines anderen Problems unerwartet NULLs generiert, wird Ihr 'robuster' Code das Problem verbergen. Es ist manchmal angemessenes Verhalten, aber kein sicherer Standard.
GoatInTheMachine
1
@GoatInTheMachine - Ich bin nicht einverstanden, dass es eine sichere Standardeinstellung ist, aber Sie haben Recht, dass das Problem dadurch ausgeblendet wird und dass ein solches Verhalten manchmal unerwünscht ist. Nach meiner Erfahrung ist es besser, das Problem zu verbergen (oder Unit-Tests zu vertrauen, um Probleme mit anderem Code zu erkennen), als Ihre App durch Auslösen unerwarteter Ausnahmen zum Absturz zu bringen. Ich meine wirklich - wenn der aufrufende Code kaputt genug ist, um Nullen zu senden, wie ist die Chance, dass sie mit Ausnahmen richtig umgehen?
Telastyn
10
Wenn etwas kaputt geht, sei es mein Code, der eines anderen Kollegen oder eines Kunden, dann wäre es mir lieber, wenn der Code sofort fehlschlägt und die Leute davon wüssten, dass er über einen unbestimmten Zeitraum in einem möglicherweise inkonsistenten Zustand weitergeführt wird. In der Lage zu sein, ist natürlich manchmal ein Luxus und nicht immer angemessen, aber es ist der Ansatz, den ich bevorzuge.
GoatInTheMachine
1
@GoatInTheMachine - Ich stimme für viele Dinge zu, aber Sammlungen sind in der Regel unterschiedlich. Null als leere Sammlung zu behandeln, ist kein schrecklich inkonsistentes oder überraschendes Verhalten.
Telastyn
8
Stellen Sie sich vor, Sie haben ein Array, das aus einem JSON-Dokument ausgefüllt ist, das über eine Webanforderung empfangen wurde, und in der Produktion hat einer der API-Clients plötzlich ein Problem, bei dem er teilweise ungültiges JSON sendet, wodurch Sie ein Array als NULL deserialisieren. Im schlimmsten Fall hat der Code eine NULL-Referenzausnahme ausgelöst, sobald die erste fehlerhafte Anfrage eingegangen ist, die Sie bei der Protokollierung sehen und dann dem Client sofort mitteilen, dass die fehlerhaften Anfragen behoben werden sollen, ODER Ihr Code lautet "oh, das Array ist leer, nichts machen!" und schrieb stillschweigend "Einkaufskorb hatte keine Artikel" an die DB für ein paar Wochen?
GoatInTheMachine
10

Wenn Sie die wiederholten Nullprüfungen stören, können Sie Ihre eigene Erweiterungsmethode 'IsNullOrEmpty ()' erstellen, um die String-Methode mit diesem Namen zu spiegeln, und sowohl die Nullprüfung als auch den Aufruf von .Any () in einen einzigen Aufruf umbrechen.

Ansonsten ist die Lösung, die von @ 17 von 26 in einem Kommentar unter Ihrer Frage erwähnt wird, ebenfalls kürzer als die 'Standard'-Methode und für jeden, der mit der neuen nullbedingten Syntax vertraut ist, einigermaßen klar.

if(myCollection?.Any() == true)
Theo Brinkman
quelle
4
Sie könnten auch ?? falseanstelle von verwenden == true. Dies scheint mir eindeutiger (klarer) zu sein, da es nur den Fall von behandelt nullund nicht wirklich ausführlicher ist. Plus- ==Checks auf Boolesche Werte lösen normalerweise meinen Würgereflex aus. =)
jpmc26
2
@ jpmc26: Ich weiß nicht viel über C #. Warum ist nicht myCollection?.Any()genug?
Eric Duminil
6
@EricDuminil Aufgrund der Funktionsweise des nullbedingten Operators wird myCollection?.Any()effektiv ein nullable-Boolean anstelle eines gewöhnlichen Boolean (dh bool? Anstelle von bool) zurückgegeben. Es gibt keine implizite Konvertierung von Bool? zu boolen, und es ist ein Bool, den wir in diesem speziellen Beispiel brauchen (um als Teil der ifBedingung zu testen ). Daher müssen wir entweder explizit mit truedem Null-Koaleszenz-Operator (??) vergleichen oder diesen verwenden.
Steven Rands
@ jpmc26 ?? false ist schwerer zu lesen als myCollection? .Any () == true. Unabhängig von Ihrem Würgereflex ist == wahr, was Sie implizit testen möchten. ?? false fügt nur zusätzliches Rauschen hinzu, und Sie müssen immer noch die Bewertung in Ihrem Kopf vornehmen, was passiert, wenn null, dass null == true fehlschlagen würde, fast ohne dass der Leser sich dessen überhaupt bewusst sein muss ?..
Ryan The Leach
2
@ RyanTheLeach Ich muss viel genauer darüber nachdenken, ob ==es tatsächlich funktionieren wird. Ich weiß schon intuitiv, was mit einem Booleschen passiert, wenn es nur sein kann trueoder false; Ich muss nicht einmal darüber nachdenken. Aber rein nullin die Mischung, und jetzt muss ich herausfinden, ob das ==richtig ist. ??Stattdessen wird nur der nullFall beseitigt trueund auf und reduziert false, was Sie bereits sofort verstehen. Wenn ich meinen Würgereflex zum ersten Mal sehe, besteht mein unmittelbarer Instinkt darin, ihn einfach zu löschen, da er normalerweise unbrauchbar ist, was Sie nicht wollen.
jpmc26
8

Bin ich falsch, als Client dieses Codes zu erwarten, dass zB ((int []) null) .Any () false zurückgeben sollte?

Wenn Sie sich über Erwartungen wundern, müssen Sie über Absichten nachdenken.

null bedeutet etwas ganz anderes als Enumerable.Empty<T>

Wie in Erik Eidts Antwort erwähnt , gibt es einen Bedeutungsunterschied zwischen nullund einer leeren Sammlung.

Schauen wir uns zunächst an, wie sie verwendet werden sollen.

In dem von den Microsoft-Architekten Krzysztof Cwalina und Brad Abrams verfassten Buch Framework Design Guidelines: Konventionen, Redewendungen und Muster für wiederverwendbare .NET-Bibliotheken (2. Auflage) wird die folgende bewährte Methode angegeben:

X KEINE NULL-Werte von Sammlungseigenschaften oder von Methoden zurückgeben, die Sammlungen zurückgeben. Geben Sie stattdessen eine leere Auflistung oder ein leeres Array zurück.

Stellen Sie sich vor, Sie rufen eine Methode auf, die letztendlich Daten aus einer Datenbank abruft: Wenn Sie ein leeres Array erhalten oder Enumerable.Empty<T>dies einfach bedeutet, dass Ihr Probenraum leer ist, dh Ihr Ergebnis ist eine leere Menge . Empfangen nullin diesem Zusammenhang würde jedoch einen Fehlerzustand bedeuten .

Entsprechend der Kartoffel-Analogie von Dan Wilson ist es sinnvoll, Fragen zu Ihren Daten zu stellen, auch wenn es sich um eine leere Menge handelt . Aber es macht viel weniger Sinn, wenn es keinen Satz gibt .

Søren D. Ptæus
quelle
1
Ob sie unterschiedliche Bedeutungen haben, hängt stark vom Kontext ab. Für die Zwecke der vorliegenden Logik stehen sie häufig für äquivalente Konzepte. Nicht immer , aber oft.
jpmc26
1
@ jpmc26: Ich denke, der Sinn dieser Antwort ist zu sagen, dass sie durch Codierungsrichtlinien für c # eine andere Bedeutung haben. Sie können immer den richtigen Code eingeben, wobei null und leer gleich sind, wenn Sie möchten. In einigen Fällen können Sie auch das Gleiche tun, unabhängig davon, ob es leer oder leer ist. Dies bedeutet jedoch nicht, dass sie dasselbe bedeuten.
Chris
@ Chris Und ich sage, dass Codierungsrichtlinien von Leuten, die die Sprache entworfen haben, nicht als Ersatz für die Bewertung Ihres Codes im Kontext Ihrer Anforderungen, der Bibliotheken, die Sie verwenden, und der anderen Entwickler, mit denen Sie zusammenarbeiten, verwendet werden können. Die Idee, dass sie im Kontext niemals gleichwertig sein werden, ist ein himmlischer Idealismus. Sie sind für die vorliegenden Daten im Allgemeinen möglicherweise nicht gleichwertig, können jedoch für die Generierung eines Ergebnisses in einer bestimmten Funktion, die Sie schreiben, gleichwertig sein. in diesem Fall, Sie müssen beide unterstützen , sondern sicherzustellen , dass sie das gleiche Ergebnis erzeugen.
jpmc26
Ich mag verwirrt sein von dem, was Sie sagen, aber wenn null und leer gleichbedeutend sind, um ein Ergebnis zu erzeugen, sehe ich nicht, warum Sie nicht einfach nur separate is null- und empty-Checks verwenden würden, anstatt eine Methode zu wollen, die beides auf einmal tut, aber erlaubt Ihnen nicht, sie in anderen Situationen zu unterscheiden ...
Chris
@ Chris Ich beziehe mich auf verschiedene Kontexte (die oft Bereichen entsprechen). Betrachten Sie zum Beispiel eine Funktion. nullund leer kann zu demselben Rückgabewert für die Funktion führen, aber das bedeutet nicht, dass nullund leer genau dasselbe im aufrufenden Bereich bedeuten.
jpmc26
3

Es gibt viele Antworten, die erklären, warum nullund leer unterschiedlich sind, und genug Meinungen, die versuchen , beide zu erklären, warum sie unterschiedlich behandelt werden sollten oder nicht. Sie fragen jedoch:

Bin ich falsch, als Client dieses Codes zu erwarten, dass zB ((int []) null) .Any () false zurückgeben sollte?

Es ist eine völlig vernünftige Erwartung . Sie haben genauso recht wie jemand anderer, der sich für das aktuelle Verhalten einsetzt. Ich stimme der aktuellen Implementierungsphilosophie zu, aber die treibenden Faktoren beruhen nicht nur auf kontextunabhängigen Überlegungen.

Vorausgesetzt, dass Any()ohne Prädikat im Wesentlichen Count() > 0ist, was erwarten Sie dann von diesem Snippet?

List<int> list = null;
if (list.Count > 0) {}

Oder ein Generikum:

List<int> list = null;
if (list.Foo()) {}

Ich nehme an, Sie erwarten NullReferenceException.

  • Any()Wenn es sich um eine Erweiterungsmethode handelt, die sich nahtlos in das erweiterte Objekt integrieren lässt, ist es am wenigsten überraschend, eine Ausnahme auszulösen.
  • Nicht jede .NET-Sprache unterstützt Erweiterungsmethoden.
  • Sie können immer anrufen Enumerable.Any(null)und dort erwarten Sie auf jeden Fall ArgumentNullException. Es ist die gleiche Methode und muss mit - möglicherweise - ALLEN anderen im Framework übereinstimmen.
  • Der Zugriff auf ein nullObjekt ist ein Programmierfehler . Das Framework sollte Null nicht als magischen Wert erzwingen . Wenn Sie es so verwenden, liegt es in Ihrer Verantwortung, damit umzugehen.
  • Es ist eine Meinung , du denkst in eine Richtung und ich denke in eine andere Richtung. Framework sollte so wenig wie möglich einbezogen werden.
  • Wenn Sie einen speziellen Fall haben, müssen Sie konsequent sein : Sie müssen mehr und mehr Entscheidungen mit hoher Meinung über alle anderen Erweiterungsmethoden Count()treffen Where(). Wenn eine einfache Entscheidung zu sein scheint, ist dies nicht der Fall . Was ist Max()? Es löst eine Ausnahme für eine leere Liste aus, sollte es nicht auch für eine eine auslösen null?

Was Bibliotheksdesigner vor LINQ taten, war, explizite Methoden einzuführen, wenn nullein gültiger Wert (zum Beispiel String.IsNullOrEmpty()) vorliegt, dann MÜSSEN sie mit der vorhandenen Designphilosophie übereinstimmen . Das heißt, wenn auch ziemlich trivial zu schreiben, zwei Methoden EmptyIfNull()und EmptyOrNull()könnte praktisch sein.

Adriano Repetti
quelle
1

Jim soll Kartoffeln in jeder Tüte lassen. Sonst werde ich ihn töten.

Jim hat eine Tüte mit fünf Kartoffeln drin. Befinden sich .Any()Kartoffeln in der Tüte?

"Ja", sagst du. <= wahr

Ok, Jim lebt dieses Mal.

Jim nimmt alle Kartoffeln heraus und isst sie. Befinden sich Kartoffeln in der Tüte?

"Nein", sagst du. <= false

Zeit Jim zu töten.

Jim verbrennt die Tüte komplett im Feuer. Befindet sich jetzt eine () Kartoffel in der Tüte?

"Es gibt keine Tasche." <= ArgumentNullException

Sollte Jim leben oder sterben? Nun, wir haben das nicht erwartet, also brauche ich ein Urteil. Ist es ein Fehler, Jim damit davonkommen zu lassen oder nicht?

Sie können Anmerkungen verwenden, um zu signalisieren, dass Sie auf diese Weise keine Null-Spielereien ertragen.

public bool Any( [NotNull] List bag ) 

Aber Ihre Werkzeugkette muss es unterstützen. Das bedeutet, dass Sie wahrscheinlich trotzdem Schecks schreiben werden.

kandierte_orange
quelle
0

Wenn Sie das so sehr stört, schlage ich eine einfache Erweiterungsmethode vor.

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

Jetzt können Sie dies tun:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

... und das Flag wird auf gesetzt false.

John Wu
quelle
2
Natürlich ist die returnAussage wie return source ?? Enumerable.Empty<T>();
folgt
Dies beantwortet die gestellte Frage nicht.
Kenneth K.
@ KennethK.aber es scheint das vorgestellte Problem zu lösen.
RQDQ
0

Dies ist eine Frage zu den Erweiterungsmethoden von C # und ihrer Entwurfsphilosophie. Ich denke, die beste Möglichkeit, diese Frage zu beantworten, besteht darin, die Dokumentation von MSDN zum Zweck der Erweiterungsmethoden zu zitieren :

Mit Erweiterungsmethoden können Sie vorhandenen Typen Methoden "hinzufügen", ohne einen neuen abgeleiteten Typ zu erstellen, den ursprünglichen Typ neu zu kompilieren oder auf andere Weise zu ändern. Erweiterungsmethoden sind eine spezielle statische Methode, werden jedoch so aufgerufen, als wären sie Instanzmethoden des erweiterten Typs. Für Client-Code, der in C #, F # und Visual Basic geschrieben wurde, besteht kein offensichtlicher Unterschied zwischen dem Aufrufen einer Erweiterungsmethode und den Methoden, die tatsächlich in einem Typ definiert sind.

Im Allgemeinen empfehlen wir, Erweiterungsmethoden sparsam und nur dann zu implementieren, wenn dies erforderlich ist. Client-Code, der einen vorhandenen Typ erweitern muss, sollte dies nach Möglichkeit tun, indem ein neuer Typ erstellt wird, der von dem vorhandenen Typ abgeleitet ist. Weitere Informationen finden Sie unter Vererbung.

Wenn Sie eine Erweiterungsmethode zum Erweitern eines Typs verwenden, dessen Quellcode Sie nicht ändern können, besteht das Risiko, dass eine Änderung der Implementierung des Typs zu einem Absturz Ihrer Erweiterungsmethode führt.

Wenn Sie Erweiterungsmethoden für einen bestimmten Typ implementieren, beachten Sie die folgenden Punkte:

  • Eine Erweiterungsmethode wird niemals aufgerufen, wenn sie dieselbe Signatur wie eine im Typ definierte Methode hat.
  • Erweiterungsmethoden werden auf Namespace-Ebene in den Geltungsbereich aufgenommen. Wenn Sie beispielsweise mehrere statische Klassen haben, die Erweiterungsmethoden in einem einzigen Namespace enthalten Extensions, werden diese durch die using Extensions;Direktive alle in den Geltungsbereich aufgenommen.

Zusammenfassend lässt sich sagen, dass Erweiterungsmethoden so konzipiert sind, dass sie einem bestimmten Typ Instanzmethoden hinzufügen, auch wenn die Entwickler dies nicht direkt können. Und da Instanzmethoden Erweiterungsmethoden immer überschreiben, wenn sie vorhanden sind (wenn sie mithilfe der Instanzmethodensyntax aufgerufen werden), sollte dies nur erfolgen, wenn Sie keine Methode direkt hinzufügen oder die Klasse erweitern können. *

Mit anderen Worten, eine Erweiterungsmethode sollte genau wie eine Instanzmethode funktionieren, da sie möglicherweise von einem Client zu einer Instanzmethode gemacht wird. Und weil eine Instanzmethode werfen sollte, wenn das Objekt, für das sie aufgerufen wird null, ist , sollte dies auch die Erweiterungsmethode tun.


* Als Randbemerkung ist dies genau die Situation, mit der die Designer von LINQ konfrontiert waren: Als C # 3.0 veröffentlicht wurde, gab es bereits Millionen von Clients, die System.Collections.IEnumerableund verwendeten System.Collections.Generic.IEnumerable<T>, sowohl in ihren Sammlungen als auch in foreachSchleifen. Diese Klassen zurück IEnumeratorObjekte , die nur hatten die beiden Methoden Currentund MoveNext, so das Hinzufügen alle zusätzliche erforderliche Instanzmethoden, wie zum Beispiel Count, Anyusw., würde diese Millionen von Kunden brechen. Also, um diese Funktionalität bereitzustellen (vor allem , da es in Bezug auf die umgesetzt werden können Currentund MoveNextmit relativer Leichtigkeit), veröffentlichten sie es als Erweiterungsmethoden, die auf jede derzeit angewendet werden , können bestehendeIEnumerableInstanz und kann auch von Klassen auf effizientere Weise implementiert werden. Hätten die Entwickler von C # beschlossen, LINQ am ersten Tag freizugeben, wäre es als Instanzmethode von bereitgestellt worden IEnumerable, und sie hätten wahrscheinlich eine Art System entworfen, das Standardschnittstellenimplementierungen dieser Methoden bereitstellt.

Der Hansinator
quelle
0

Ich denke, der springende Punkt hier ist, dass Sie durch das Zurückgeben von false anstelle des Auslösens einer Ausnahme Informationen verschleiern, die für zukünftige Leser / Modifikatoren Ihres Codes relevant sein könnten.

Wenn es möglich ist, dass die Liste null ist, würde ich vorschlagen, einen separaten Logikpfad dafür zu haben, da ansonsten in Zukunft jemand eine listenbasierte Logik (wie ein Add) zum else {} Ihres if hinzufügen könnte, was zu einem führt ungewollte Ausnahme, dass sie keinen Grund hatten, vorherzusagen.

Lesbarkeit und Wartbarkeit sind die Trümpfe, dass ich jedes Mal eine zusätzliche Bedingung schreiben muss.

Callum Bradbury
quelle
0

Im Allgemeinen schreibe ich den größten Teil meines Codes, um anzunehmen, dass der Anrufer dafür verantwortlich ist, mir keine Nulldaten zu übergeben, und zwar hauptsächlich aus den in „ Nein zu Null sagen“ (und anderen ähnlichen Beiträgen) genannten Gründen . Das Konzept von Null ist oft nicht gut verstanden, und aus diesem Grund ist es ratsam, Ihre Variablen so früh wie möglich zu initialisieren. Angenommen, Sie arbeiten mit einer vernünftigen API, sollten Sie im Idealfall niemals einen Nullwert zurückerhalten, sodass Sie niemals nach Null suchen müssen. Der Nullpunkt ist, wie in anderen Antworten angegeben, sicherzustellen, dass Sie ein gültiges Objekt (z. B. eine "Tasche") haben, mit dem Sie arbeiten können, bevor Sie fortfahren. Dies ist kein Merkmal von Any, sondern ein Merkmal der Spracheselbst. Sie können die meisten Vorgänge für ein Nullobjekt nicht ausführen, da es nicht vorhanden ist. Es ist so, als würde ich Sie bitten, in meinem Auto zum Geschäft zu fahren, außer ich besitze kein Auto, also haben Sie keine Möglichkeit, mit meinem Auto zum Geschäft zu fahren. Es ist unsinnig, eine Operation an etwas durchzuführen, das buchstäblich nicht existiert.

Es gibt praktische Fälle für die Verwendung von null, z. B. wenn Sie buchstäblich nicht über die angeforderten Informationen verfügen (z. B. wenn ich Sie nach meinem Alter gefragt habe, lautet die wahrscheinlichste Antwort an dieser Stelle "Ich weiß nicht" ", was ein Nullwert ist). In den meisten Fällen sollten Sie jedoch zumindest wissen, ob Ihre Variablen initialisiert sind oder nicht. Und wenn nicht, dann würde ich empfehlen, dass Sie Ihren Code verschärfen müssen. Das allererste, was ich in einem Programm oder einer Funktion mache, ist, einen Wert zu initialisieren, bevor ich ihn benutze. Es ist selten, dass ich nach Nullwerten suchen muss, da ich garantieren kann, dass meine Variablen nicht Null sind. Es ist eine gute Angewohnheit, sich darauf einzulassen, und je häufiger Sie daran denken, Ihre Variablen zu initialisieren, desto seltener müssen Sie nach null suchen.

Phyrfox
quelle