Jak daleko powinien być „var” i zerowy operator koalescencyjny „??” być rozrywką bez ograniczania czytelności?

23

Wiem, że tytuł pytania jest bardzo subiektywny, ale spotkałem się z użyciem ??operatora przez moich rówieśników, gdzie jednocześnie nie byłem bardzo zadowolony / komfortowo z zastosowania varnowego kodu.

Argumentem podanym za pomocą ??operatora było usunięcie czytelności kodu.

Moje pytanie brzmi: czy to samo nie dzieje się, kiedy zaczynasz używać var?

Numan
źródło
49
Jeśli „programista” nie jest w stanie zrozumieć ??zerowego operatora koalescencji po jego wyjaśnieniu, nie powinien być dozwolony w pobliżu kodu produkcyjnego.
CaffGeek
12
Proponuję zmienić operatora z ?? na IfNullThen. ? może być IfSoChoose,: jest ElseChoose. + to Dodaj. - to Odejmij, chyba że jest to jednoargumentowe, to jest Negatywne. - dzieli się między DecrementBeforeUsing i DecrementAfterUsing. W C ++ możesz to zrobić za pomocą makr.
Lee Louviere,
7
@Xaade: To ironia, prawda? ... dobrze? ... Boże, niech to będzie ironia (a ja jestem ateistą).
Steven Jeuris,
10
@StevenJeuris To może być sarkazm , ale to nie ironia .
Kirk Broadhurst
1
@KirkBroadhurst: Poprawiłem się , zawsze myliłem tych dwóch.
Steven Jeuris, 10.11.11

Odpowiedzi:

53

Zerowy operator koalescencyjny (??)

Osobiście nie widzę żadnych wad korzystania z tego operatora. Rozważ następujące trzy przykłady kodu, od „łatwych” do „złożonych” nowych operatorów.

Bez magii:

bool isNameSet = false;
string name;
if ( isNameSet )
{
    Console.WriteLine( name );
}
else
{
    Console.WriteLine( "No name set." );
}

Operator trójskładnikowy:

bool isNameSet = false;
string name;
Console.WriteLine( isNameSet ? name : "No name set." );

Brak koalescencji:

string name = null;
Console.WriteLine( name ?? "No name set." );

Powodem, dla którego wymyślono te operatory, jest to, że reprezentują one bardzo popularne operacje programistyczne . Nie chcę ich używać, ponieważ nie jesteś do nich przyzwyczajony, to tylko upór . Języki ewoluują, funkcje ewoluują, naucz się ich używać!

słowo kluczowe var

Mam nieco inne zdanie na temat słowa kluczowego var. Typ zmiennej często zapewnia dodatkowe informacje o kodzie. Uważam, że ukrywanie typu za pomocą słowa kluczowego var czasami sprawia, że ​​kod jest mniej czytelny. Wiesz mniej, czego się spodziewać, bez korzystania z automatycznego uzupełniania lub najechania kursorem na identyfikatory, aby zobaczyć, jakie są w rzeczywistości. Moim zdaniem powoduje to, że kod jest wolniejszy do odczytu / zapisu.

Używam tego słowa kluczowego, gdy okazuje się, że ten typ nie zawiera wielu dodatkowych informacji.

  • Głównie w pętlach Foreach , których nauczyłem się od Resharpera, ponieważ jest to tam ustawienie. Przez większość czasu wiesz, jaki rodzaj kolekcji przemierzasz, więc wiesz, że oczekujesz przedmiotów z tej kolekcji.
  • Zapytania Linq . Rezultatem zapytań linq są często bardzo złożone typy ogólne. Wyświetlenie tego typu powoduje więcej szkody niż pożytku.
  • Długie nazwy maszynowe, które są po prostu inicjowane ich konstruktorem. Możesz już powiedzieć, jaki jest typ, patrząc na konstruktor.

Jako przykład ostatniej instrukcji:

ThisIsSomeSpecializedTypeRightHere duplication =
    new ThisIsSomeSpecializedTypeRightHere();
var justAsReadable =
    new ThisIsSomeSpecializedTypeRightHere();  // Less duplication.

// But I still prefer the following ...
int number = 9;
SomeCreatedType foo = Factory.CreateSomeType();
Steven Jeuris
źródło
24
Ten ostatni przykład jest dokładnie wtedy, gdy słowo kluczowe var może się wyróżniać. Wyobraź sobie, że kod jest zaśmiecony w całym kodzie. Factory.CreateSomeTypezwraca IEnumerable<SomeType>. Pewnego dnia, z jakiegokolwiek powodu, zmienia się w powrót SomeType[]. Jeśli używałeś var, to tylko rekompilacja.
pdr
1
Źle to zrozumiałem, a potem poszedłem poszukać jedzenia. Numpty! Lepszym przykładem byłoby użycie twojego. Co jeśli Factory.CreateSomeType()zmiany zwrócą ISomeType?
pdr
4
@pdr: To najprawdopodobniej oznacza również zmianę interfejsu, a zachowanie należy również dostosować / dodać. Jeśli jest to prosta zmiana nazwy, oczywiście masz automatyczną zmianę nazwy. Jeśli zmieni się „umowa”, raczej widzę łamanie kodu, więc mogę zobaczyć, czy muszę coś zmienić, czy nie.
Steven Jeuris
2
Myślę, że bardziej prawdopodobne jest, że jeśli zwróci się konkretny typ, zwraca interfejs, to po prostu dopuszcza inne konkretne typy z tym samym interfejsem (zgodne ze wzorcem fabrycznym). Ale jeśli umowa ulegnie zmianie, Twój kod się zepsuje - choć może się okazać, że zepsuje się tylko w kilku przypadkach, w których ma to znaczenie, a nie wszędzie.
pdr
1
@Tom Hawtin, wszyscy wiemy, że nie da się go nullcałkowicie uniknąć . Co więcej, im więcej szukam alternatyw null, tym bardziej zdaję sobie sprawę, że używanie nulljest czasem najczystszym rozwiązaniem. Mój podany przykład nie jest wcale taki zły IMHO, i uważam, że jest to właściwe użycie typów zerowalnych.
Steven Jeuris
16

var pozwala na mniej szczegółowy kod, co zwiększa czytelność. Moim zdaniem czytelność nie powinna być mierzona przez to, ile szczegółów widzisz, ale ile szczegółów jest ukrytych na pierwszy rzut oka. Oprócz przykładów Linq, var umożliwia pisanie kaczek na poziomie kodu źródłowego, biorąc pod uwagę następujący przykład:

...
foreach (var message in messages) {
  var label = Factory.CreateLabel();
  label.Text = message;
  Controls.Add(label);
}
...

Kogo to obchodzi, jaki jest rodzaj etykiety, o ile zapewnia ona właściwość Text?

Codism
źródło
Ktoś próbuje zrozumieć, czy jest to np. Etykieta winforms czy etykieta WPF.
Steven Jeuris
14
@Steven Jeuris: to zwykle znana informacja kontekstowa. Jeśli nie, podchodzisz do kodu w niewłaściwy sposób.
Codism
Ktoś, kto chce wiedzieć, czy jest to domyślna etykieta WPF, czy jakaś niestandardowa etykieta? :)
Steven Jeuris
14
1. Czy ta osoba powinna się tym naprawdę przejmować, o ile jest to etykieta z właściwością Text? 2. Jeśli jest to jeden z rzadkich przypadków, które NAPRAWDĘ Mają dobry powód, aby się tym przejmować, zapytaj Intellisense. 3. Jeśli edytują kod w IDE bez Intellisense, prawdopodobnie mają znacznie większe niedogodności niż „var” :)
KutuluMike
4
Ktoś ze słabym poczuciem abstrakcji.
Jim Balter,
16

Nie mam żadnych poważnych komentarzy na temat ??operatora, ponieważ nigdy nie używałem języka z takim operatorem. Jeśli chodzi o to var, ludzie programują w językach takich jak Ruby, Python, Perl i PHP, które mają w pełni niejawne pisanie przez cały czas. Mieszkańcy tych języków zdają sobie sprawę, że formalnym typem zmiennej jest zwykle nieistotny hałas. Bardziej interesuje Cię to, co może zrobić zmienna , tj. Jej interfejs strukturalny / kaczka.

Podobnie, programuję głównie w języku D. D jest wpisany statycznie, ale ma autosłowo kluczowe, które jest równoważne var. Używanie go wszędzie jest uważane za idiomatyczne, chyba że zauważysz konkretną potrzebę podkreślenia rodzaju czegoś czytnikowi lub (poprzez niejawną konwersję) kompilatorowi. Z wyjątkiem sytuacji, gdy używany jest jako typ zwracanej funkcji (jest to dozwolone w D), nigdy nie uważałem, że utrudnia to czytelność, ponieważ myślę głównie w kategoriach interfejsów strukturalnych / kaczych, a nie typów formalnych / nominalnych, kiedy programuję.

Co więcej, używanie IMHO varwszędzie, gdzie jest to możliwe, jest dobrym przykładem SUCHEGO. Rodzaj czegoś powinien być określony w jednym i tylko jednym miejscu, a następnie automatycznie propagowany za każdym razem, gdy trzeba go propagować. Jeśli użyjesz, vara formalny typ zmiennej musi się w pewnym momencie zmienić, niezbędne zmiany zostaną automatycznie rozpowszechnione wszędzie tam, gdzie jest to konieczne, o ile program jest nadal poprawny pod względem typu, a nie programista musi ręcznie zmieniać każdą instancję . To jest Dobra Rzecz (TM).

dsimcha
źródło
13

Myślę, że to ostatecznie pytanie do zespołu. Jeśli nie jest to czytelne dla nikogo w moim zespole, nie będę go używał, chociaż mógłbym spróbować kilka razy, aby przekonać ich przykładami lub wskazując podobne skróty (takie jak + =), które uważają za bardziej czytelne.

Jeśli jednak pracuję sam, to znajdę? i var doskonale czytelne bez ograniczeń. Parzysty

var a = b ?? c ?? d ?? e ?? "";

jest dla mnie dość jednoznaczny.

Operatory trójskładnikowe i dynamicto oczywiście inna sprawa.

pdr
źródło
4
Użyłbym tutaj `String.Empty '.
Job
1
@Jac, szczerze mówiąc, ja też. Miałem zamiar włożyć coś do tego sznurka, a potem nic nie mogłem wymyślić :)
pdr
4
var a = b ?? c ?? d ?? e ?? String.Empty ?? "";
Lee Louviere
2
@Xaade Nigdy nie możesz być wystarczająco pewny. Ale tak naprawdę używanie String.Empty lub „” jest osobistą preferencją. Używam „”, ponieważ jest krótszy ...
Carra,
12
@Xaade - jeśli String.Emptykiedykolwiek powróci null, czeka nas cholernie dużo zepsutego kodu.
Jesse C. Slicer,
10

Podany argument za użyciem ?? operator był, zabiera czytelność w kodzie.

Krótko o tym myśląc, ten komentarz mówi mi, że osoba, która go komentuje, nie rozumie go tak dobrze, jak powinna. Jest to najprawdopodobniej prawda w niektórych sytuacjach, ale ogólnie rzecz biorąc, nie pomijam czegoś, co zespół C # najwyraźniej uważał za wystarczająco ważne, aby dodać ze względu na „czytelność”. Ja to w tej samej kategorii if(boolean_object)vs if(boolean_object == true). Niektórzy twierdzą, że drugi jest bardziej czytelny, ale w rzeczywistości jest to po prostu dodanie dodatkowego kodu do czytania / pisania, a w niektórych sytuacjach może być bardziej mylące (pomyśl if(boolean_object != false))

Moje pytanie brzmi: czy to samo nie dzieje się, kiedy zaczynasz używać var?

Zespół C # pozwolił ci nie definiować czegoś, co wiesz, co to będzie. O ile nie muszę absolutnie określać, jaka będzie zmienna (albo absolutnie ważne jest, aby zwracany obiekt był typu x, albo naprawdę nie jest czytelny), używam var. var x = "MY_STRING";Wiem, że to ciąg od patrzenia na to. Prawdę mówiąc, tak naprawdę nie dbam o to, że to sznurek, o ile robi to, czego potrzebuję. Zdefiniowanie typu zmiennej jest dla mojej korzyści, a nie kompilatora. Jeśli coś jest nie tak, kompilator powie mi, kiedy działa, jeśli mam zły typ zmiennej.

kemiller2002
źródło
0

varKluczowe jest, moim zdaniem, pokonał stosowane w sytuacjach, dla których to było oryginalne wprowadzono - zapytań LINQ. W tych zapytaniach typ zwracanego wyniku często ma dużą, skomplikowaną nazwę, która jest trudna do ustalenia z góry i nie pomaga czytelnikowi zrozumieć, co robi Twój kod.

Jednak robienie tego var text = "Some text " + variableName + "some more text."jest po prostu leniwe.

EDYCJA: @Jorg Skoczyłeś na celowo uproszczoną odpowiedź, ale nic nie dodałeś do dyskusji. OK, co powiesz na lepszy przykład: var items = doc.DocumentElement.FirstChild.ChildNodes;jeśli możesz dowiedzieć się, na podstawie tego typu, dam ci ciasteczko.

Josh Earl
źródło
18
Jeśli nie możesz zrozumieć, że "Some text "jest to stringa, masz o wiele większe problemy do zmartwienia niż liczba vars w kodzie.
Jörg W Mittag
3
Problem nie jest var; problemem jest kiepska nazwa zmiennej „items”. Jeśli użyjesz dobrej nazwy zmiennej, nie ma w tym nic złego var.
Kyralessa,
3
Zgaduję (bez patrzenia), że elementy to zbiór węzłów XML, który powinien mieć wszystkie właściwości i metody zbioru węzłów XML, i to naprawdę wszystko, co muszę wiedzieć.
KutuluMike,
Jeśli chcesz wiedzieć, jaki typ var oznacza i nie możesz od razu stwierdzić, po prostu najedź na niego myszą. Nie trzeba wiele wymyślić, aby to zrobić.
scrwtp
1
„jest po prostu leniwy” - to nie jest argument przeciwko temu. W rzeczywistości nie jest wcale inteligentny.
Jim Balter,
0

Mam podstawowy problem z używaniem var.

W tym przykładzie wszystko działa obok siebie, ale problem dotyczy naprawdę dużych rozwiązań ze współdzielonymi bibliotekami lub projektami.

Rozważ to:

public MyFirstObject GetMeAnObject() {
    return new MyFirstObject();
}

public void MainProgram() {
    var theObject = GetMeAnObject();
    theObject.PerformOperation();
}

Co się stanie, jeśli GetMeAnObject zostanie zmieniony przez kogoś innego, w zależności od jego potrzeb?

public MySecondObject GetMeAnObject() {
    return new MySecondObject();
}

Metoda MainProgram będzie miała duży czerwony błąd na .PerformOperation (). Co się stało? PerformOperation działał wcześniej doskonale. Patrzymy na metody w obiekcie i po prostu zniknął bez śladu. Był tam ostatni raz i potrzebujemy tej metody. Mógłbyś spędzać dużo czasu na ściganiu ogona i próbowaniu dowiedzieć się, dlaczego, jeśli MyFirstObject ma metodę o nazwie PerformOperation, nie można jej teraz zobaczyć. Każdy „wie”, że GetMeAnObject zwraca MyFirstObject, więc nie ma sensu tego sprawdzać.

Jeśli wpisałeś jawnie obiekt, wówczas wystąpiłby błąd nieprawidłowego rzutowania w linii, która wywołuje GetMeAnObject, i byłoby oczywiste, że GetMeAnObject zwraca typ, który nie jest tym, czego oczekujesz.

Krótko mówiąc, wyraźne oświadczenie oznacza, że ​​wiesz, co oznaczają błędy. Nieprawidłowa obsada oznacza, że ​​oczekiwano jednego typu, a inny typ został zwrócony. Nierozpoznany członek oznacza, że ​​członek został nierozpoznany.

Hugh Phoenix-Hulme
źródło
10
Współpracownik dokonał przełomowej zmiany w kodzie, która nie jest objęta testami, i uważasz, że problem jest var? Nie można oczekiwać, że język ochroni się przed tego rodzaju zachowaniami - co jeśli bezpośrednio zmodyfikują MyFirstObject? Nadal by się zepsuł, ale żadna składnia nie mogłaby cię przed tym uratować. varUznałbym to za siłę nawet: co, jeśli zamiast zwracać MySecondObject, zwracasz teraz IMyFirstObject?
Phoshi,
2
„Wszyscy” wiedzą, że GetMeAnObject zwraca MyFirstObject, więc nie ma sensu tego sprawdzać. ” Nie mogę sobie wyobrazić scenariusza programowania, w którym zależałoby od mojej pamięci tego, co GetMeAnObject, jeśli debuguję. Gdybym sprawdził, że PerformOperation nie ma, zobaczyłbym kod i nie dotyczy innej klasy. Jeśli w IDE klasa wyświetliłaby instancję, patrzę na typ obiektu. W rzeczywistości, gdy kompilator powie mi błąd, powie „klasa MySecondObject nie ma operacji PerformOperation”. Skąd to wszystko wiadomo?
Muhammad Alkarouri,