Dlaczego programiści C # otwierają nawiasy otwierające? [Zamknięte]

44

Spędziłem większość ostatnich kilku lat pracując głównie z C # i SQL. Każdy programista, z którym pracowałem przez ten czas, miał zwyczaj umieszczać nawias otwierający funkcji lub instrukcji przepływu w nowym wierszu. Więc ...

public void MyFunction(string myArgument)
{
     //do stuff
}

if(myBoolean == true)
{
    //do something
}
else
{
    //do something else
}

Zawsze uderzyło mnie to, jak mało miejsca to jest, szczególnie w instrukcjach if / else. Wiem, że w późniejszych wersjach C # istnieją alternatywy, takie jak:

if(myBoolean == true)
    //do something on one line of code

Ale mało kto ich używał. Wszyscy zrobili kręcone szelki na nowej linii.

Potem wróciłem do robienia JavaScript po długiej nieobecności. W mojej pamięci, programiści JavaScript robili dokładnie to samo curly-nawiasy klamrowe-nowa linia, ale przy wszystkich fantazyjnych nowych bibliotekach i innych rzeczach, większość programistów umieszcza nawias otwierający po deklaracji:

function MyJavaScriptFunction() {
    //do something
}

Widać w tym sens, ponieważ odkąd stosowanie zamknięć i wskaźników funkcji stało się popularne w JavaScript, oszczędza dużo miejsca i czyni rzeczy bardziej czytelnymi. Zastanawiałem się więc, dlaczego nie było postrzegane jako zrobione w C #. W rzeczywistości, jeśli wypróbujesz powyższą konstrukcję w Visual Studio 2013, faktycznie sformatuje ją dla Ciebie, umieszczając otwierający nawias w nowej linii!

Teraz widziałem to pytanie w Code Review SE: https://codereview.stackexchange.com/questions/48035/questions-respactions-let-me-tell-you-about-you, w którym dowiedziałem się, że w Javie język, którego nie znam zbyt dobrze, uważa się za rygorystyczny sposób otwierania nawiasów klamrowych zaraz po deklaracji, w nowoczesnym stylu JavaScript.

Zawsze rozumiałem, że C # był pierwotnie wzorowany na Javie i był zgodny z wieloma tymi samymi podstawowymi standardami kodowania. Ale w tym przypadku wydaje się, że nie. Zakładam więc, że musi być dobry powód: jaki jest powód? Dlaczego programiści C # (i Visual Studio) wymuszają otwieranie nawiasów klamrowych w nowej linii?

Bob Tway
źródło
30
Konwencja jest po prostu tym.
Oded
19
Visual Studio niczego nie wymusza, styl kodu można konfigurować. Coś musi być domyślne. To, co domyślne jest „lepsze”, to przejazdy rowerowe najwyższego rzędu.
Phoshi,
23
Klamra na końcu linii to starożytny standard K&R C. Kernighan i Ritchie wymyślili język C, gdy ekran komputera miał tylko 25 linii (23, jeśli dodasz linie nagłówka i statusu). Tak naprawdę już tak nie jest, prawda? Ważna jest konsekwencja w całym projekcie. Nie również badania naukowe pokazujące, że klamra z własnej linii (wcięcie na tym samym poziomie co w kodzie, w rzeczywistości) poprawia kodu zrozumieniem mimo tego, co ludzie myślą, że myślą o estetyce.
Craig
10
Nawiasem mówiąc, C # zawsze wspierał ocenę jednego wiersza kodu po warunkowym. W rzeczywistości to jedyna rzecz, jaką robi, nawet teraz. Umieszczenie kodu w nawiasach klamrowych po wyrażeniu rozgałęziającym czyni z tej instrukcji goto (kompilator tworzy zakres za pomocą instrukcji jmp). C ++, Java, C # i JavaScript są w mniejszym lub większym stopniu oparte na C, w większości z tymi samymi podstawowymi regułami analizy. W tym sensie C # nie jest „oparty na Javie”.
Craig
10
Na marginesie, if(myBoolean == true)nie ma dla mnie sensu. Skoro już to robimy, a nie if ((myBoolean == true) == true)? Po prostu if (myBoolean)i to wystarczy. Przepraszam, mój wkurzony zwierzak.
Konrad Morawski,

Odpowiedzi:

67

Klamrą na końcu linii jest starożytny standard K&R C, z książki Briana Kernighana i Dennisa Ritchiego The C Programming Language , którą opublikowali w 1978 roku po wspólnym wynalezieniu systemu operacyjnego UNIX i języka programowania C (myślę, że C był zaprojektowane głównie przez Ritchie), w AT&T.

Kiedyś toczyły się wojny o „jeden prawdziwy styl aparatów ortodontycznych”.

Ritchie wynalazł język C, a Kernighan napisał pierwszy samouczek, gdy na ekranach komputera pojawiło się tylko kilka wierszy tekstu. W rzeczywistości prace nad UNICS (późniejszym UNIX) rozpoczęły się od DEC PDP-7, w którym jako interfejs użytkownika zastosowano maszynę do pisania, drukarkę i taśmę papierową. UNIX i C zostały ukończone na PDP-11, z 24-wierszowymi terminalami tekstowymi. Tak więc przestrzeń pionowa była naprawdę na wagę złota. Wszyscy dzisiaj mamy nieco lepsze wyświetlacze i drukarki o wyższej rozdzielczości, prawda? To znaczy, nie wiem o tobie, ale mam teraz przed sobą trzy (3) 24-calowe wyświetlacze 1080p. :-)

Ponadto, tak wiele z tej małej książki The C Programming Language to próbki kodu, które umieszczenie nawiasów klamrowych na końcach linii zamiast na ich własnych liniach rzekomo pozwoliło zaoszczędzić znaczną ilość pieniędzy na drukowaniu.

Naprawdę ważna jest spójność w całym projekcie lub przynajmniej w danym pliku kodu źródłowego.

Nie również badania naukowe pokazujące, że klamra z własnej linii (wcięcie na tym samym poziomie co w kodzie, w rzeczywistości) poprawia zrozumienie kodu wbrew temu, co ludzie myślą, że myślą o estetyce. Wyjaśnia czytelnikowi, wizualnie i instynktownie, który kod działa w jakim kontekście.

if( true )
    {
    // do some stuff
    }

Nawiasem mówiąc, C # zawsze wspierał ocenę pojedynczego polecenia po wyrażeniu rozgałęziającym. W rzeczywistości to jedyna rzecz, jaką robi, nawet teraz. Wstawienie kodu w nawiasy klamrowe po wyrażeniu rozgałęziającym sprawia, że ​​jedno polecenie jest goto (kompilator tworzy zakres za pomocą instrukcji jmp). C ++, Java, C # i JavaScript są w mniejszym lub większym stopniu oparte na C, w większości z tymi samymi podstawowymi regułami analizy. W tym sensie C # nie jest „oparty na Javie”.

Podsumowując, jest to trochę kwestia religijna / wojenna. Ale tam badania co czyni go dość oczywiste, że umieszczenie kodu w blokach poprawia ludzkie zrozumienie. Kompilator nie przejmował się tym mniej. Ale jest to również związane z powodem, dla którego nigdy nie umieszczam wiersza kodu za gałęzią bez nawiasów klamrowych - dla mnie lub innego programisty jest to zbyt łatwe, aby umieścić tam inną linię kodu później i poślizgnąć się na fakcie, że nie będzie wykonać w tym samym kontekście z linią tuż przed nią lub po niej.

EDYCJA : Wystarczy spojrzeć na błąd Apple,goto fail aby znaleźć doskonały przykład tego dokładnie problemu, który miał bardzo poważne konsekwencje w świecie rzeczywistym.

if( true )
    doSomething();

staje się...

if( true )
    doSomething();
    doSomethingElse();

W takim przypadku doSomethingElse()wykonuje się za każdym razem, niezależnie od wyniku testu, ale ponieważ jest wcięty do tego samego poziomu co doSomething()instrukcja, łatwo go przeoczyć. To nie jest tak naprawdę dyskusyjne; badania potwierdzają to. Jest to duże źródło błędów wprowadzonych do kodu źródłowego podczas konserwacji.

Mimo to przyznaję, że składnia zamknięcia JavaScript wygląda trochę głupio z nawiasami klamrowymi na własnych liniach ... estetycznie. :-)

Craig
źródło
13
Część dotycząca bloków w warunkowym jest nieco myląca. W C warunek zawsze ma jedną instrukcję, ale blok (inaczej instrukcja złożona) jest rodzajem instrukcji . C # podąża za tym precedensem. W każdym razie ocena warunkowa zawsze implikuje pewien rodzaj warunkowej instrukcji goto lub rozgałęzienia, ze względu na liniowy charakter wszystkich popularnych zestawów instrukcji. Warunek bez bloku nie różni się jakościowo od warunku z blokiem (z wyjątkiem zakresu).
amon
3
@Craig, pamiętajcie, było to w Bell Labs, kiedy Bell Labs był zatrudniony do „robienia interesujących rzeczy”, które niekoniecznie musiały być praktyczne do niczego. Umieszczaj inteligentnych ludzi w budynku, powiedz im, aby przeprowadzili ciekawe badania, odsuń ich od tego, co robią, nie jest „interesujące”, i, co zabawne, CIEKAWE rzeczy (takie jak tranzystory i Unix) mają tendencję do wyskakiwania. Na początku DARPA skupiało się na tym samym celu i sfinansowało DUŻO dobrej pracy w ramach podstawowych badań.
John R. Strohm,
3
@Craig: W dawnych czasach Pascal / Delphi zwykle unikałem tego w przypadku bloków jednowierszowych, ponieważ Pascal używa begini endzamiast nawiasów klamrowych, co sprawia, że ​​takich błędów naprawdę trudno przeoczyć, a także bardziej surowe w zakresie umieszczania średników - ale wydając 4 dni w zeszłym roku debugowanie problemu we wbudowanym C, który ostatecznie wynikał z braku umiejscowienia nawiasów klamrowych ... Myślę, że powinna istnieć opcja kompilatora, która zobowiązuje nawiasy klamrowe do instrukcji sterujących!
Mark K Cowan
12
Proszę podać odniesienia do „... badań naukowych wykazujących, że nawias klamrowy na własnej linii ...”. Bez co najmniej dwóch referencji (inaczej byłoby to badanie , a nie studia ), po prostu strzelał losowo w powietrze.
ErikE,
2
@Craig, czy możesz podać jakieś wskazówki, jak znaleźć wspomniane badania?
Grzegorz Adam Kowalski
30

Powodem jest to, że programiści C # robią to, ponieważ jest to domyślne ustawienie automatycznego formatowania programu Visual Studio. Chociaż to ustawienie można zmienić, większość ludzi tego nie robi, dlatego wszyscy programiści w zespole muszą zdecydować się na większość.

Co do tego, dlaczego jest to ustawienie domyślne w Visual Studio, nie wiem.

Timwi
źródło
15
Jest to ustawienie domyślne w programie Visual Studio, ponieważ był to ogólnie akceptowany styl kodowania w firmie Microsoft w drodze powrotnej, a tak naprawdę w przynajmniej niektórych zespołach firmy Microsoft styl był nie tylko nawiasami klamrowymi na własnych liniach, ale nawiasami klamrowymi na własnych liniach i wcięcia. Jest to również opcja w Visual Studio (osobiście wolę).
Craig
20
@Craig: Ewwwwwwwww
Lekkość
5
@PreferenceBean Hmm. Osobiście nie lubię stylu K&R, ponieważ nawiasy są po prostu zatracone w hałasie na końcach linii, a tekst wydaje mi się nieporęczny, co nie jest tylko preferencją estetyczną, ale ma rzeczywisty wpływ na czytelność / zrozumienie. Lubię pionowe białe znaki. Było trochę inaczej na 12-calowych monitorach z 24 wierszami tekstu w dawnych czasach, kiedy przestrzeń pionowa była cenna.
Craig
10
@Craig: To nie jest usprawiedliwienie dla wcięcia nawiasów klamrowych: P
Lekkość ściga się z Moniką
2
@Craig Czy nieszczęście dla pracowników Microsoft?
Mateen Ulhaq
18

Jak postawiłem hipotezę w tej odpowiedzi tutaj - https://softwareengineering.stackexchange.com/a/159081/29029 - Podejrzewam, że decyzja o zastosowaniu Allmana może być oznaką dziedzictwa Pascala, biorąc pod uwagę, że Hejlsberg stworzył Delphi zanim zaprojektował C # . To samo co wielkość Pascala dla nazw metod.

Ja osobiście wierzę w powiedzenie „w Rzymie postępujcie tak, jak Rzymianie”. Używam Allman's w C #, ale K&R w Javie. Jestem do tego tak przyzwyczajony, że zmiana konwencji w jakikolwiek sposób jest dla mnie niepokojąca.

Uważam, że pewna różnorodność konwencji jest w rzeczywistości korzystna dla „wielojęzycznego” programisty, ponieważ pomaga zapamiętać język, w którym obecnie kodują, i ułatwia różnicowanie różnych nawyków. Można powiedzieć, że jest to mentalny odpowiednik pamięci mięśniowej.

Konrad Morawski
źródło
7
W rzeczywistości +1 za wiersz „programista wielojęzyczny”. Naprawdę wolę aparaty ortodontyczne na ich własnych liniach i wcięciach. Jednak w pracy nad, na przykład, projektami ASP.NET (MVC), uważam, że podoba mi się C # w stylu „właściwym”, a JavaScript w stylu „JavaScript”. Jest to trochę zawadiacki, ale szczerze mówiąc, składnia JavaScript jest bardziej atrakcyjna z nawiasami na końcu linii. Jestem więc niewolnikiem estetyki jak każdy inny ...
Craig
PascalCase dla nazw metod był standardem w firmie Microsoft na długo zanim Anders Hejlsberg opuścił Borland, aby zostać wybitnym inżynierem oprogramowania w firmie Microsoft.
Craig
2
Co ciekawe, używam Allmana we wszystkich językach bloków nawiasów klamrowych. Jednak coraz bardziej interesuje mnie, jak może wyglądać konwersja wstępnego przetwarzania wcięć w bloki klamrowe w Javie. Kod „java” może wyglądać wyjątkowo czysto, gdy tylko go opamiętam. Myślę, że Python może mieć rację: naprawdę nie ma wielkiego powodu dla nawiasów klamrowych w języku.
tgm1024,
Używanie białych znaków do kontrolowania przepływu programu to szaleństwo. Python jest obecnie popularny, ale ten aspekt Pythona był trochę szalony od momentu jego pierwszego pojawienia się.
Craig,
@ Fan Craiga Pythona tutaj, próbujący zrobić coś w javascript. Patrząc na twoją drugą odpowiedź i twoją edycję dotyczącą goto failużywania wcięcia do kontrolowania przepływu programu, ludzie naturalnie chcą to zrobić, więc widzisz to, co się wykonuje. Jeśli jest źle wcięte, to po prostu nie działa. Konieczność odkodowania, co oznaczają te nawiasy klamrowe, niezależnie od tego, czy faktycznie odzwierciedlają wcięcia, to dodatkowa praca wykraczająca poza zrozumienie programu. Wcięcie jest zbędne dla nawiasów klamrowych, więc dlaczego programiści używają go, jeśli nawiasy klamrowe są przydatne? Głębokie zagnieżdżanie jest niezrozumiałe w żadnym języku. Po prostu mówię.
Neil_UK
14

Konwencja, którą kojarzysz z Javą, to styl K&R lub „One true brace style” i oryginalnie pochodzi z C. Ta styl wcięcia strony z czcigodnego pliku Jargon pokazuje, ile lat ma to rozróżnienie.

Zawsze myślałem, że styl Allmana i jego warianty są odzwierciedleniem tego, jak autorzy myślą o kodzie, podnosząc ograniczniki bloków do poziomu słów kluczowych podobnych do tego, w jaki sposób niektóre języki używają „początku” i „końca” wokół bloków kodu.

John Bickers
źródło
Mam wrażenie, że w dzisiejszych czasach OTBS (jeden prawdziwy styl nawiasów klamrowych) oznacza również, że nigdy nie pomijam nawiasów klamrowych po instrukcjach sterujących, nawet w przypadku pojedynczych wierszy kodu. Zobacz błąd Apple goto fail.
Craig