Instanziieren von Nullobjekten mit dem Nullkoaleszenzoperator


12

Stellen Sie sich das folgende typische Szenario vor:

if(myObject == null) {
    myObject = new myClass();
}

Ich frage mich, was man von dem folgenden Ersatz mit dem Null-Koaleszenz-Operator hält:

myObject = myObject ?? new myClass();

Ich bin nicht sicher, ob ich das zweite Formular verwenden soll. Es scheint eine nette Abkürzung zu sein, aber das myObject = myObjectKonstrukt am Anfang scheint ein bisschen nach Code zu riechen.

Ist das eine vernünftige Sache, oder gibt es eine bessere Abkürzung, die ich vermisse? Oder vielleicht "Es sind drei Zeilen, komm drüber hinweg!"?

Edit: Wie bereits erwähnt, ist die Bezeichnung eines typischen Szenarios möglicherweise etwas übertrieben. Normalerweise stoße ich auf diese Situation, wenn ich eine Entität aus einer Datenbank mit einer untergeordneten Referenztypeigenschaft abrufe, die möglicherweise noch nicht ausgefüllt ist:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();

15
Neulinge glauben, dass solche Dinge "hackisch" sind, erfahrene Entwickler nennen es einfach "idiomatisch".
Doc Brown

Wenn das Objekt wie ein Wertobjekt aussieht ("hat Wertsemantik", idiomatisch gesprochen), MyClasssollte es ein public static readonlyMitglied eines EmptyWerts seines Typs liefern . Siehe String.Emptyunter MSDN .
Rwong


5
Gibt es keinen ??=Operator?
Aviv

1
@aviv Ich wünschte, es gäbe!
Loren Pechtel

Antworten:


16

Ich benutze die ganze Zeit den Null-Koaleszenz-Operator. Ich mag die Prägnanz davon.

Ich finde, dass dieser Operator dem ternären Operator (A? B: C) ähnelt. Es braucht ein wenig Übung, bevor das Lesen zur Selbstverständlichkeit wird, aber wenn Sie sich erst einmal daran gewöhnt haben, ist die Lesbarkeit meiner Meinung nach besser als bei den Langhandversionen.

Außerdem ist die von Ihnen beschriebene Situation nur ein Szenario, in dem der Bediener nützlich ist. Es ist auch praktisch, Konstrukte wie das folgende zu ersetzen:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

oder

return value != null ? value : otherValue;

mit

return value ?? otherValue;

Stimmen Sie der Verwendung dieses Operators im Allgemeinen definitiv zu. Wenn ich jedoch Hinweise darauf sehe, dass ich es verwende, ist das normalerweise so myObject = myObject2 ?? myObject3. Ich frage mich insbesondere, ob der Operator verwendet wird, um ein Objekt auf sich selbst zu setzen, es sei denn, es ist null.
grin0048

2
Ich denke, dass die gleiche Argumentation in beiden Fällen gilt. Es ist eine präzisere Art, dieselbe Logik auszudrücken.
17 von 26

Ich erinnere mich, dass ich mir die IL für jede dieser drei Formen einmal angesehen habe. Das erste und das zweite waren identisch, aber das dritte wurde so optimiert, wie es nullein Vergleich sein könnte.
Jesse C. Slicer

2

Ich frage mich insbesondere, ob der Operator verwendet wird, um ein Objekt auf sich selbst zu setzen, es sei denn, es ist null.

Das ?? operator wird als Nullkoaleszenzoperator bezeichnet und wird zum Definieren eines Standardwerts für nullfähige Werttypen oder Referenztypen verwendet. Es gibt den linken Operanden zurück, wenn der Operand nicht null ist. Andernfalls wird der richtige Operand zurückgegeben.

myObject = myObject ?? new myObject(); - instantiate default value of object

Weitere Details und Codebeispiele zum Null-Koaleszenz-Operator - MSDN-Artikel .

Wenn Sie die more than nullableBedingung überprüfen , können Sie alternativ den ternären Operator verwenden.

Ternärer Operator

Sie können sich auch Folgendes ansehen : Operator . Es wird ternärer oder bedingter Operator genannt. Der bedingte Operator (? :) gibt abhängig vom Wert eines Booleschen Ausdrucks einen von zwei Werten zurück.

Ein nullwertfähiger Typ kann einen Wert enthalten oder undefiniert sein. Das ?? Der Operator definiert den Standardwert, der zurückgegeben wird, wenn einem nicht nullfähigen Typ ein nullfähiger Typ zugewiesen wird. Wenn Sie versuchen, einem nicht nullwertfähigen Werttyp einen nullwertfähigen Werttyp zuzuweisen, ohne das Zeichen ?? Operator erzeugen Sie einen Fehler beim Kompilieren. Wenn Sie eine Umwandlung verwenden und der Typ des nullbaren Werts derzeit nicht definiert ist, wird eine InvalidOperationException-Ausnahme ausgelöst.

Ein Codebeispiel von MSDN -?: Operator (C # -Referenz) :

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";

Nimmt man also das Beispiel aus der Frage und wendet den bedingten Operator an, ergibt sich so etwas wie myObject = (myObject == null) ? new myClass() : myObject. Wenn ich den bedingten Operator nicht besser verwende, scheint es das gleiche "Problem" zu geben, wenn ein Objekt auf sich selbst gesetzt wird (wenn es nicht null ist) wie der null-koaleszierende Operator - und es ist weniger präzise.
grin0048

Wenn Sie nach mehr als einer Bedingung suchen (nicht null und größer als null), wird dies vom bedingten Operator ausgeführt. Jedoch würde gerade Nullprüfung für arbeiten? Operator.
Yusubov

2

Ich denke nicht, dass dieses Szenario typisch ist (oder zumindest sein sollte).

Wenn der Standardwert eines Feldes nicht sein soll null, stellen Sie ihn im Feldinitialisierer ein:

myClass myObject = new myClass();

Wenn die Initialisierung komplizierter ist, legen Sie sie im Konstruktor fest.

Wenn Sie myClassnur dann erstellen möchten, wenn Sie es tatsächlich benötigen (z. B. weil das Erstellen sehr lange dauert), können Sie Folgendes verwenden Lazy<T>:

Lazy<myClass> myObject = new Lazy<myClass>();

(Dies ruft den Standardkonstruktor auf. Wenn die Initialisierung komplizierter ist, übergeben Sie Lambda, das erstellt wird, myClassan den Lazy<T>Konstruktor.)

Um auf den Wert zuzugreifen, verwenden Sie myObject.Value, wodurch die Initialisierung aufgerufen wird, wenn Sie zum ersten Mal auf diesen zugreifen myObject.Value.


IMO Lazy Creation ist nur dann nützlich, wenn Sie das resultierende Objekt nicht unbedingt benötigen. Wenn ich Lazy Creation nur dann verwende, wenn ich ein abgeleitetes Ergebnis habe, das ich zurücksetze, wenn sich etwas ändert, und ich weiß, dass ich das gleiche Ergebnis sehr oft benötigen werde und das Nachrechnen ist teuer. andere weise ich will einfach nicht mit dem Null - Check stören
Ratsche Freak

@ratchetfreak Ja, deshalb habe ich zuerst den Feldinitialisierer vorgeschlagen.
Svick

Es gibt auch andere Fälle. Was ich gerade sehe: Der erste Durchgang setzt die Ausnahmefälle. Der zweite Durchgang legt die Standardeinstellungen für alles andere fest. ?? = würde die Linie halbieren.
Loren Pechtel

1

Ich verwende es besonders gerne ??in Verbindung mit optionalen Methodenparametern. Ich beschränke die Notwendigkeit von Überladungen und stelle sicher, dass das Ding einen Wert hat.

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.