Tworzenie instancji obiektów zerowych za pomocą operatora zerowania

12

Rozważ następujący typowy scenariusz:

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

Zastanawiam się, co myśli się o następującym zastąpieniu za pomocą operatora zerowania:

myObject = myObject ?? new myClass();

Nie jestem pewien, czy powinienem używać drugiego formularza. Wygląda na to, że jest to ładny skrót, ale myObject = myObjectkonstrukcja na początku wydaje się być trochę zapachem kodu.

Czy jest to rozsądne, czy może istnieje lepszy skrót, którego mi brakuje? A może: „To trzy linie, przejedź to!”?

Edycja: Jak już wspomniano, być może nazwanie tego typowym scenariuszem jest przesadą. Zwykle spotykam się z tą sytuacją, gdy pobieram encję z bazy danych, która ma właściwość typu odwołania potomnego, która może być jeszcze zapełniona:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();
grin0048
źródło
15
Początkujący uważają, że takie rzeczy są „hackerskie”, bardziej doświadczeni deweloperzy nazywają to „idiomatycznym”.
Doc Brown
Jeśli obiekt wygląda jak obiekt wartości („ma semantykę wartości”, mówiąc idiomatycznie), MyClasspowinien podać element public static readonlyczłonkowski Emptywartości tego typu. Zobacz String.Emptyw MSDN .
rwong
5
Czy nie ma ??=operatora?
aviv
1
@aviv Chciałbym, żeby tam był!
Loren Pechtel,

Odpowiedzi:

16

Cały czas używam zerowego operatora koalescencyjnego. Podoba mi się zwięzłość tego.

Uważam, że ten operator ma charakter podobny do operatora trójskładnikowego (A? B: C). Zajmuje trochę praktyki, zanim czytanie tego jest drugą naturą, ale kiedy już się do tego przyzwyczaisz, czuję, że czytelność poprawia się w porównaniu z wersjami długimi.

Opisana sytuacja to tylko jeden scenariusz, w którym operator jest przydatny. Przydatne jest również zastępowanie takich konstrukcji:

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

lub

return value != null ? value : otherValue;

z

return value ?? otherValue;
17 z 26
źródło
Zdecydowanie zgadzam się ogólnie na temat korzystania z tego operatora. Kiedy widzę odniesienia do korzystania z niego, zwykle jest to coś w rodzaju myObject = myObject2 ?? myObject3. Zastanawiam się konkretnie nad użyciem operatora do ustawienia sobie obiektu, chyba że ma on wartość null.
grin0048,
2
Myślę, że w obu przypadkach obowiązuje to samo rozumowanie. To bardziej zwięzły sposób wyrażenia tej samej logiki.
17 z 26
Pamiętam, jak raz spojrzałem na IL dla każdej z tych trzech form. Pierwszy i drugi były identyczne, ale trzeci został zoptymalizowany w sposób, który można by uznać nullza porównanie.
Jesse C. Slicer
2

Zastanawiam się konkretnie nad użyciem operatora do ustawienia sobie obiektu, chyba że ma on wartość null.

?? operatorNazywa operator null koalescencji i służy do określenia wartości domyślnej wartości dla różnych rodzajów i typów pustych odniesienia. Zwraca operand po lewej stronie, jeśli operand nie jest pusty; w przeciwnym razie zwraca odpowiedni operand.

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

Więcej szczegółów i przykładowy kod na temat operatora zerowego koalescencji - artykuł MSDN .

Jeśli sprawdzisz more than nullablewarunek, alternatywnie możesz użyć operatora trójskładnikowego.

Operator trójskładnikowy

Możesz także spojrzeć na : Operator . Nosi nazwę operatora trójskładnikowego lub warunkowego . Operator warunkowy (? :) zwraca jedną z dwóch wartości w zależności od wartości wyrażenia logicznego.

Typ dopuszczający wartości zerowe może zawierać wartość lub może być niezdefiniowany. ?? operator definiuje wartość domyślną, która ma być zwracana, gdy typ zerowalny jest przypisany do typu zerowalnego. Jeśli spróbujesz przypisać typ wartości dopuszczającej wartości zerowe do typu wartości nie dopuszczającej wartości zerowej bez użycia ?? operator wygeneruje błąd czasu kompilacji. Jeśli użyjesz rzutowania, a typ wartości zerowalnej jest obecnie niezdefiniowany, zostanie zgłoszony wyjątek InvalidOperationException.

Przykład kodu z MSDN -?: Operator (odwołanie do C #) :

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

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";
Jusubow
źródło
Biorąc przykład z pytania i stosując operator warunkowy, mielibyśmy coś takiego myObject = (myObject == null) ? new myClass() : myObject. Tak więc, chyba że brakuje mi lepszego użycia operatora warunkowego, wydaje się, że ma ten sam „problem” z ustawieniem dla siebie obiektu (jeśli nie jest on zerowy) jak operator koalescencyjny zerowy - i jest mniej zwięzły.
grin0048,
Jeśli sprawdzasz więcej niż jeden warunek (nie zerowy i większy od zera) - zrobi to operator warunkowy. Jednak sprawdzanie wartości zerowej działałoby dla? operator.
Yusubov,
2

Nie sądzę, że ten scenariusz jest (a przynajmniej powinien być) typowy.

Jeśli nie chcesz, aby domyślna wartość niektórych pól była inna null, ustaw ją w inicjalizatorze pola:

myClass myObject = new myClass();

Lub, jeśli inicjalizacja jest bardziej skomplikowana, ustaw ją w konstruktorze.

Jeśli chcesz tworzyć myClasstylko wtedy, gdy jest to potrzebne (np. Ponieważ tworzenie go zajmuje dużo czasu), możesz użyć Lazy<T>:

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

(To wywołuje domyślny konstruktor. Jeśli inicjalizacja jest bardziej skomplikowana, przekaż lambda, która tworzy myClassdo Lazy<T>konstruktora).

Aby uzyskać dostęp do wartości, użyj myObject.Value, który wywoła inicjalizację, jeśli jest to pierwszy dostęp myObject.Value.

svick
źródło
IMO leniwe tworzenie jest przydatne tylko wtedy, gdy nie masz gwarancji, że potrzebujesz wynikowego obiektu, tylko czas, gdy używam leniwego tworzenia jest, gdy mam wyprowadzony wynik kasowany, który resetuję, gdy coś się zmienia i wiem, że bardzo potrzebuję tego samego wyniku recalc jest drogi. z drugiej strony po prostu nie chcę zawracać sobie głowy zerowym czekiem
maniak zapadkowy
@ratchetfreak Tak, dlatego najpierw zasugerowałem inicjator pola.
svick,
Są też inne przypadki. Na co teraz patrzę: pierwszy przebieg ustawia przypadki wyjątków. Drugi przebieg ustawia wartości domyślne dla wszystkiego innego. ?? = przeciąłby linię na pół.
Loren Pechtel
1

Szczególnie podoba mi się używanie ??w połączeniu z opcjonalnymi parametrami metody. Ograniczam potrzebę przeciążeń i upewniam się, że rzecz ma wartość.

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
radarbob
źródło