Operator zerowego koalescencji w c # pozwala skrócić kod
if (_mywidget == null)
return new Widget();
else
return _mywidget;
Aż do:
return _mywidget ?? new Widget();
Ciągle odkrywam, że użytecznym operatorem, który chciałbym mieć w języku C #, byłby taki, który pozwoliłby ci zwrócić właściwość obiektu lub inną wartość, jeśli obiekt jest pusty. Więc chciałbym wymienić
if (_mywidget == null)
return 5;
else
return _mywidget.Length;
Z:
return _mywidget.Length ??! 5;
Nie mogę przestać myśleć, że musi istnieć jakiś powód, dla którego ten operator nie istnieje. Czy to zapach kodu? Czy jest jakiś lepszy sposób na napisanie tego? (Zdaję sobie sprawę z wzorca zerowego obiektu, ale wydaje się, że używanie go do zastąpienia tych czterech wierszy kodu jest przesadą).
c#
code-smell
null
Ben Fulton
źródło
źródło
??!
jest operatorem w C ++. :-)Odpowiedzi:
Bardzo chcę funkcji języka C #, która pozwala mi bezpiecznie wykonywać x.Prop.SomeOtherProp.ThirdProp bez konieczności sprawdzania każdego z nich pod kątem wartości null. W jednej bazie kodu, w której musiałem często wykonywać tego rodzaju „głębokie przejścia własności”, napisałem metodę rozszerzenia o nazwie Navigate, która połknęła NullReferenceExceptions i zamiast tego zwróciła wartość domyślną. Więc mógłbym zrobić coś takiego:
Minusem tego jest to, że ma inny zapach kodu: połknięte wyjątki. Jeśli chcesz zrobić coś takiego „dobrze”, możesz wziąć lamdba jako wyrażenie i zmodyfikować wyrażenie w locie, aby dodać kontrole zerowe wokół każdego akcesorium właściwości. Wybrałem „szybki i brudny” i nie wdrożyłem go w ten „lepszy” sposób. Ale może kiedy będę miał zapasowe cykle myślowe, wrócę do tego i gdzieś opublikuję.
Aby bardziej bezpośrednio odpowiedzieć na twoje pytanie, domyślam się, że powodem, dla którego nie jest to funkcja, jest to, że koszt wdrożenia tej funkcji przewyższa korzyść z jej posiadania. Zespół C # musi wybrać, na których funkcjach się skoncentrować, a ta jeszcze nie dotarła na szczyt. Tylko zgaduję, że nie mam żadnych informacji poufnych.
źródło
Wierzę, że będziesz w stanie to zrobić z C # 6 po prostu teraz:
Zwróć uwagę na zerowy operator warunkowy
?.
po lewej stronie._mywidget?.Length
zwraca null, jeśli_mywidget
jest null.Prosty operator trójskładnikowy może być łatwiejszy do odczytania, jak sugerowali inni.
źródło
To tylko spekulacje, dlaczego tego nie zrobili. W końcu nie wierzę? był w pierwszej wersji C #.
W każdym razie po prostu użyłbym operatora warunkowego i nazwałbym go dniem:
?? jest tylko skrótem do operatora warunkowego.
źródło
5
odpowiedź na wypadek, gdyby go nie było. Zanim zaczniesz prosić o specjalnych operatorów, musisz spojrzeć na swój projekt.Wygląda jak operator trójskładnikowy:
źródło
Osobiście uważam, że zależy to od czytelności. W pierwszym przykładzie
??
tłumaczy się całkiem ładnie: „Jesteś tam? Nie, zróbmy to”.W drugim przykładzie
??!
jest wyraźnie WTF i nic nie znaczy dla programisty ... obiekt nie istnieje, więc nie mogę dostać się do właściwości, więc zwrócę 5. Co to w ogóle oznacza? Co to jest 5? Jak dojść do wniosku, że 5 to dobra wartość?Krótko mówiąc, pierwszy przykład ma sens ... nie ma go, nowy . W drugim przypadku jest otwarta do debaty.
źródło
(foo() != ERROR)??!??! cerr << "Error occurred" << endl;
5
. Naprawdę uważam, że jest więcej problemów, które musisz zrobić w ten sposób, podczas gdy w twoim pierwszym przykładzie potrzeba jest dość oczywista, szczególnie w takich rzeczach, jak menedżerowie pamięci podręcznej.Myślę, że ludzie są zbyt pochłonięci „5”, którą powracasz ... być może 0 byłoby lepiej :)
W każdym razie myślę, że problem polega na tym, że
??!
tak naprawdę nie jest to samodzielny operator. Zastanów się, co to znaczy:W takim przypadku nie ma to sensu: ma sens tylko wtedy, gdy lewy operand jest akcesorium własności. A co z tym:
Jest tam akcesorium do nieruchomości, ale nadal nie sądzę, aby miało to sens (jeśli
myWidget
taknull
, czy to znaczy, że w ogóle nie dzwonimyFoo()
?), Czy to tylko błąd?Myślę, że problem polega na tym, że po prostu nie pasuje tak naturalnie do języka, jak
??
.źródło
Ktoś stworzył klasę narzędzi, która zrobiłaby to za ciebie. Ale nie mogę tego znaleźć. Znalazłem coś podobnego na forach MSDN (spójrz na drugą odpowiedź).
Przy odrobinie pracy możesz go rozszerzyć, aby ocenić wywołania metod i inne wyrażenia, których próbka nie obsługuje. Można go również rozszerzyć, aby zaakceptować wartość domyślną.
źródło
Rozumiem skąd pochodzisz. Wygląda na to, że wszyscy są owinięci wokół osi zgodnie z podanym przykładem. Chociaż zgadzam się, że liczby magiczne są złym pomysłem, dla niektórych właściwości można by użyć odpowiednika zerowego operatora koalicyzacji.
Na przykład masz obiekty związane z mapą i chcesz, aby były na właściwej wysokości. Mapy DTED mogą mieć dziury w swoich danych, więc można mieć wartość zerową, a także wartości w zakresie od -100 do ~ 8900 metrów. Możesz chcieć czegoś w stylu:
W tym przypadku zerowy operator koalescencji zapełniłby domyślną wysokość, jeśli Współrzędne nie zostały jeszcze ustawione lub obiekt Coordinates nie mógłby załadować danych DTED dla tej lokalizacji.
Uważam to za bardzo cenne, ale moje spekulacje, dlaczego tego nie zrobiono, ograniczają się do złożoności kompilatora. Mogą istnieć przypadki, w których zachowanie nie byłoby tak przewidywalne.
źródło
Kiedy nie mam do czynienia z głęboko zagnieżdżonymi, użyłem tego (przed wprowadzeniem zerowego operatora koalescencyjnego w C # 6). Dla mnie to całkiem jasne, ale może dlatego, że jestem do tego przyzwyczajony, inni ludzie mogą uznać to za mylące.
Oczywiście nie zawsze ma to zastosowanie, ponieważ czasami właściwość, do której trzeba uzyskać dostęp, nie może być ustawiona bezpośrednio, lub gdy budowa samego obiektu jest kosztowna, między innymi.
Inną alternatywą jest użycie wzorca NullObject . Zdefiniuj pojedynczą instancję statyczną klasy z rozsądnymi wartościami domyślnymi i użyj jej zamiast tego.
źródło
Magiczne liczby to zwykle nieprzyjemny zapach. Jeśli 5 jest dowolną liczbą, lepiej ją przeliterować, aby była wyraźniej udokumentowana. Moim zdaniem wyjątkiem byłby wyjątek, jeśli masz zbiór wartości, które działają jako wartości domyślne w określonym kontekście.
Jeśli masz zestaw wartości domyślnych dla obiektu, możesz podklasować go, aby utworzyć domyślną instancję singletonu.
Coś w stylu: return (myobj ?? default_obj) .Length
źródło
Właściwość length zwykle jest typem wartości, więc nie może być zerowa, więc operator koalescencji zerowej nie ma tutaj sensu.
Ale możesz to zrobić za pomocą właściwości, która jest typem odniesienia, na przykład:
var window = App.Current.MainWindow ?? new Window();
źródło
Widziałem wcześniej kogoś z podobnym pomysłem, ale przez większość czasu chciałbyś również przypisać nową wartość do pola zerowego, na przykład:
więc pomysł polegał na połączeniu
??
i=
przypisania w:Myślę, że ma to większy sens niż używanie wykrzyknika.
Ten pomysł nie jest mój, ale bardzo mi się podoba :)
źródło
return _obj ?? new Class();
że nie ma takiej potrzeby??=
.