Radzenie sobie z nawiasami klamrowymi

11

Programowałem zarówno w C #, jak i VB.NET od lat, ale przede wszystkim w VB. Zmieniam karierę w kierunku C # i ogólnie bardziej lubię C #.

Jednym z moich problemów jest zupa z kędzierzawego klamry. W VB każde słowo kluczowe struktury ma pasujące słowo kluczowe close, na przykład:

Namespace ...
    Class ...
        Function ...
            For ...
                Using ...
                    If ...
                        ...
                    End If
                    If ...
                        ...
                    End If
                End Using
            Next
        End Function
    End Class
End Namespace

Ten sam kod napisany w C # kończy się bardzo trudny do odczytania:

namespace ... {
    class ... {
        function ... {
            for ... {
                using ... {
                    if ... {
                        ...
                    }
                    if ... {
                        ...
                    }
                }
            }
            // wait... what level is this?
        }
    }
}

Będąc tak przyzwyczajonym do VB, zastanawiam się, czy programiści w stylu c zastosowali technikę poprawiającą czytelność i zapewniającą, że kod kończy się we właściwym „bloku”. Powyższy przykład jest stosunkowo łatwy do odczytania, ale czasami na końcu fragmentu kodu mam 8 lub więcej poziomów nawiasów klamrowych, co wymaga przewinięcia kilku stron w górę, aby dowiedzieć się, który nawias kończy blok, który mnie interesuje w.

JDB wciąż pamięta Monikę
źródło
83
Wiem, że może to zabrzmieć kaznodzieją i być może masz specjalne warunki, które tego wymagają (bo tak, czasami jest to konieczne - na szczęście takie czasy powinny być rzadkie), ale zwykle „... 8 lub więcej poziomów nawiasów klamrowych, wymagających przewijania kilka stron, aby dowiedzieć się, który nawias kończy blok, który mnie interesuje ” oznacza, że ​​kod wymaga poważnego refaktoryzacji i czyszczenia.
FrustratedWithFormsDesigner
7
Jedną rzeczą, którą widziałem i robiłem, jest na końcu nawiasu klamrowego, dodam komentarz na ten temat. Coś jak // End's using X statement.
PiousVenom,
14
@FrustratedWithFormsDesigner zwraca uwagę na fakt, że przepływ kontroli jest bałaganem i należy go przerobić. To powiedziawszy, myślę, że narzekasz bardziej na nawiasy klamrowe niż na zakres i powiedziałbym, że po prostu musisz się do tego przyzwyczaić. Uczenie się języka o znacznie innej składni niż ta, z którą przez pewien czas na pewno wygląda jak zupa, ale z praktyką, która odchodzi. Musisz tylko zapiąć pasy i zająć się nimi, dopóki mózg nie zacznie przetwarzać składni w bardziej naturalny sposób, to tam dotrzesz.
Jimmy Hoffa
2
@FrustratedWithFormsDesigner - Muszę oczyścić wiele obiektów COM w sytuacji interakcji COM. Korzystam z techniki sugerowanej w tym artykule: jake.ginnivan.net/vsto-com-interop . To z łatwością tworzy dwie lub trzy warstwy. Kiedy nakładasz na siebie pętlę for, funkcję, klasę i przestrzeń nazw (wraz z instrukcją if), możesz łatwo dostać się do kilku warstw nawiasów klamrowych.
JDB wciąż pamięta Monikę
5
@TyrionLannister - i mogą to być jedne z najszybszych komentarzy, które nie są zsynchronizowane z tym, do czego należą ... Myślę, że gdybym miał coś takiego, wolałbym, aby było generowane automatycznie (na wyświetlaczu -Tylko tylko czas, nie utrwalony) przez IDE.
Clockwork-Muse,

Odpowiedzi:

39

Umieść początkowy nawias klamrowy w tej samej „pozycji” co końcowy, tak jak poniżej:

namespace ... 
{
    class ... 
    {
        function ... 
        {
            for ... 
            {
                using ... 
                {
                    if ... 
                    {
                        ...
                    }
                    if ... 
                    {
                        ...
                    }
                }
            }
            // It's the `function` level!
        }
    }
}
Robert Harvey
źródło
15
Zgadzam się. Egipskie nawiasy powodują ból głowy.
PiousVenom,
8
Ponadto, większość IDE (prawdopodobnie) podświetla partnera klamry po kliknięciu na niego.
StuperUser,
3
@TyrionLannister: Dzięki, że w końcu dałeś mi termin na ten styl! Nigdy nie miałem dla nich dobrego imienia oprócz „źle dopasowanych nawiasów klamrowych”.
FrustratedWithFormsDesigner
3
@ Cyborgx37: Czy twoje IDE ma funkcję „Idź do pasującego nawiasu klamrowego”? Zwykle związany z klawiszem skrótu, który automatycznie przesuwa kursor do nawiasu pasującego do aktualnie podświetlonego.
FrustratedWithFormsDesigner
3
Muszę powiedzieć, że nie rozumiem, jak to ułatwia. Tak czy inaczej, wystarczy spojrzeć na ekran na poziomie wcięcia nawiasów, aż dojdzie do słowa kluczowego. I z tymi wszystkimi dodatkowymi liniami musisz teraz dalej szukać.
Blorgbeard wyszedł
14
  • W zależności od IDE: umieść kursor na nawiasie otwierającym / zamykającym, a podświetli zarówno ten, jak i odpowiedni nawias klamrowy.
  • Zwiń blok i pokaże Ci, gdzie się otwiera / zamyka.
  • Napisz mniejsze bloki kodu. Poważnie. Sprawdź Clean Codei już nigdy nie napotkasz tego problemu (i masz bardziej czytelny / możliwy do utrzymania kod).

Jedna uwaga, następująca poprawna składnia c # może pomóc w twojej konkretnej sytuacji:

using (var type = new MyDisposable1())
using (var type2 = new MyDisposable2())
{
    /* do what you will with type2 and type2 */
}
Steven Evers
źródło
Polowanie na jedną wyróżnioną postać skłoniło mnie do postawienia tego pytania.
JDB wciąż pamięta Monikę
2
@ Cyborgx37: Stąd punkt 3. Jeśli cały blok kodu mieści się na ekranie, nigdy nie musisz polować. W większości / wszystkich klasach, które piszę, jedyne pary nawiasów klamrowych, które nie pasują do ekranu, to przestrzeń nazw / klasa.
Steven Evers,
To, co sugerujesz w punktach 1 i 2, jest tym, co robię teraz ... ale wymaga to ode mnie miejsca, w którym koduję, i zaczynam manipulować moim widokiem kodu, aby dowiedzieć się, gdzie umieścić następną linię. Punkt 3 jest dobrze przyjęty, ale nie zawsze jest to możliwe, szczególnie jeśli twój kod wymaga kilku warstw usingbloków (patrz jake.ginnivan.net/vsto-com-interop )
JDB wciąż pamięta Monikę
@ Cyborgx37: Zobacz moją edycję.
Steven Evers,
1
@ Cyborgx37 Jeśli wybierzesz kolor, który wyróżnia się spośród wszystkich innych (przez jakiś czas używałem fioletowego tła i białego tekstu, IIRC), nie ma potrzeby szukania pasującego nawiasu klamrowego - to praktycznie krzyczy: „Jestem TUTAJ ! „.
CVn
5

Powszechną konwencją jest dodawanie komentarza po nawiasie zamykającym, aby wskazać strukturę, którą zamyka:

if {
   ...
} // end if

while (condition) {
   ...
} // end while

itp. Nigdy nie rozgrzewałam tej konwencji, ale niektórzy uważają ją za pomocną.

John Bode
źródło
16
Widziałem, jak ludzie to robią, a kiedy zamieniają / zmieniają początek bloku (zmieniają whilena a for, zamieniają na ifinstrukcje), prawie NIGDY nie pamiętają o aktualizacji końcowych komentarzy, czyniąc je gorszymi niż bezużytecznymi. Ta konwencja będzie prawdopodobnie przydatna tylko wtedy, gdy możesz zmusić się do utrzymywania komentarzy za każdym razem, gdy charakter dopasowanych {zmian.
FrustratedWithFormsDesigner
Tak, zrobiłem trochę tego, ale to dużo dodatkowej pracy (i hałasu). Miałem nadzieję, że będzie coś prostszego.
JDB wciąż pamięta Monikę
5
Komentarze powinny jedynie wyjaśniać, dlaczego nigdy co i jak kod robi oba te elementy, a poleganie na komentarzach w celu wyjaśnienia któregokolwiek z nich oznacza, że ​​kod jest trudny do odczytania, co jest znakiem, że kod powinien zostać poprawiony, a nie skomentowany.
Jimmy Hoffa,
1
To sprawia, że ​​zastanawiam się, dlaczego nikt nie napisał edytora, który wyświetla te komentarze, ale tak naprawdę nie włącza ich do kodu.
Brendan Long,
2
@ JimmyHoffa: Myślę, że lepszą regułą dla komentarzy jest to, że powinny one zapewniać jasność . Zwykle oznacza to odpowiedź „dlaczego”, ale może również oznaczać inne rzeczy. Nie daj się tak pochłonąć dogmatom, że powstrzymuje cię to od robienia rzeczy, które faktycznie pomagają, na przykład od czasu do czasu dodania komentarza do nawiasu zamykającego, który jest bardzo oddalony od nawiasu otwierającego.
Bryan Oakley,
5

Ogólnie rzecz biorąc, kiedy trudno jest dopasować nawiasy klamrowe w dowolnym stylu - prawdopodobnie oznacza to, że metoda jest za długa i powinna zostać ponownie uwzględniona.

MaximR
źródło
5

Myślę, że musisz wytrzymać z aparatami ortodontycznymi. W końcu staną się dla ciebie drugą naturą i będziesz się zastanawiać, jak żyłeś bez nich.

Upewnij się jednak, że są one odpowiednio wcięte i że przestrzegana jest pewna konwencja odstępów (nie ma znaczenia, która).

MrFox
źródło
Rok później ta rada brzmi prawdziwie. :)
JDB wciąż pamięta Monikę
4

Usuwam 2 poziomy zagnieżdżania, zwijając przestrzeń nazw i zakresy klas w poziomie. Zauważ, że metody są wyrównane do lewej krawędzi ekranu. Nie widzę sensu w utracie 2 poziomów wcięć w każdym pliku.

Po tym rzadko zdarza się, że zagnieżdżasz się na głębokości większej niż 4 poziomy.

namespace FooNameSpace {
class Foo {

public void bar()
{
    while(true)
    {
        while(true)
        {
            break;
        }
    }
}

public void fooBar()
{
    foreach(var item in FooList)
    {
        foreach(var b in item.Bars)
        {
            if(b.IsReady)
            {
                bar();
            }
            bar();
        }
        bar();
    }
}

}}//end class, namespace
mike30
źródło
Podoba mi się ten pomysł, ale wydaje się, że Visual Studio go nie obsługuje (przynajmniej 2008. Pod koniec roku aktualizujemy do 2012, więc mam nadzieję)
JDB wciąż pamięta Monikę
@ Cyborgx37. Edytuję tekst zewnętrznie w VIM, więc nie stanowi to problemu. Ale w Visual Studio wykonaj: Control + A, a następnie naciśnij przycisk „wcięcie mniej”. Zrób to tylko dla nowych plików. Nie przejmuj się istniejącymi plikami, ponieważ zepsują to porównania różnic w kontroli źródła.
mike30
Wpisywanie nawiasów zamykających / nawiasów klamrowych inicjuje automatyczny format VS, ale zawsze możesz nacisnąć CTRL + Z, aby odrzucić jego sugestię.
Alex In Paris,
1

Niedawno postanowiłem spróbować sformalizować dwie reguły dotyczące konstrukcji kontroli przepływu, które zasadniczo wyglądają tak:

  • Nie powinieneś mieć nic oprócz niezbędnych konstrukcji przepływu kodu
  • Powinieneś zrobić konstrukcje przepływu kodu tak małe, jak to możliwe

Właśnie z powodów, o których wspomniałeś i których jesteś wyraźnie świadomy, uważam, że są to świetne zasady, których należy przestrzegać. Istnieje kilka prostych technik, które można zastosować do ich osiągnięcia:

  • Opuść zakres jak najszybciej (dotyczy to zarówno zakresu pętli, jak i funkcji)
  • Uważaj na wady, które można złagodzić, wychodząc z funkcji z poprzedniego if i zastosuj technikę wychodzenia z zakresu, jak wspomniałem
  • Odwróć kontrole warunkowe, gdy kod wewnątrz if jest świetny niż ten na zewnątrz
  • Współczynnik kodu wewnątrz pętli do innej metody, gdy rozmiar pętli rośnie, aby zasłonić resztę metody
  • Uważaj na wszelkie zakresy, które zawierają tylko inny zakres, na przykład funkcję, której cały zakres jest wypełniony przez if bez niczego poza if

Podałem tutaj przykłady tego, jak nieprzestrzeganie tych instrukcji może zakończyć się zrobieniem tego, co powiedziałeś, i wstawieniem kodu do niewłaściwego bloku kodu, co jest złe i jest łatwą przyczyną błędów pojawiających się podczas konserwacji.

Jimmy Hoffa
źródło
Dzięki, może być blok lub dwa, które mógłbym refaktoryzować. Pomoże, ale trudno jest zreformować kilka zagnieżdżonych usingbloków.
JDB wciąż pamięta Monikę
@ Cyborgx37 faktycznie używający bloków może być wstawiany, jeśli są zagnieżdżone spełniające zakresy, spójrz tutaj stackoverflow.com/questions/1329739/... to po prostu działa jak kontrola przepływu w jednej linii konstruuje, jak możesz, jeśli (true) doSomething (); możesz także if (true) if (somethingElse) if (otherThings) {doThis (); Zrób to(); rób cokolwiek(); } i ifs zagnieżdżają się tak, jak się spodziewasz (NIE PISUJ KODU PODOBNEGO, ŻE Z MIŁOŚCI BOGA, PO PROSTU ZRÓB TO ZA POMOCĄ I INNYM heh)
Jimmy Hoffa,
Tak, wiem o zagnieżdżaniu za pomocą bloków, ale działa to tylko wtedy, gdy masz pod sobą jedną linię. W większości mojego kodu tak nie jest. Wciąż bardzo pomocny post ogólnie ... nauczył się kilku rzeczy (+1)
JDB wciąż pamięta Monikę
@ Cyborgx37 tak, zdaję sobie sprawę, że zagnieżdżanie lunety działa tylko wtedy, gdy nie masz dodatkowych bitów, co oznacza, że ​​istnieje wzorzec w sposobie używania? Czy byłoby możliwe przeniesienie części tego dodatku do konstruktora, jeśli jest on na górze, lub utylizacji, jeśli jest na dole? To znaczy, jeśli jest wzorzysty; Zgaduję, że tak może być, ponieważ poruszyłeś to jako problem, więc prawdopodobnie napotykasz je często za pomocą gniazd w kodzie.
Jimmy Hoffa
Właściwie zacząłem pracować nad implementacją wzorca fabrycznego, która otacza obiekty COM klasą „AutoCleanup”. Następnie używam pojedynczego usingwyrażenia na temat klasy fabrycznej, a gdy jest ono usuwane, automatycznie usuwa wszystkie zapakowane klasy. Jak dotąd działa dobrze i znacznie zmniejszył liczbę usinginstrukcji w moim kodzie.
JDB wciąż pamięta Monikę
1

Niestety, jest to jedna z najstarszych przyczyn działań wojennych w informatyce. Z obu stron można wysunąć uzasadnione argumenty (lepsza pionowa gospodarka nieruchomościami w porównaniu z łatwiejszą możliwością wizualnego dopasowania klamry otwierającej z klamrą zamykającą), ale w rzeczywistości prosty formatator kodu źródłowego rozwiąże wszystko za Ciebie. MS Visual C # ma wbudowany, który działa dobrze.

Ostrzegamy jednak, że jeśli pracujesz w zespole, będziesz musiał przestrzegać konwencji zastosowanych przez ten zespół, więc opłaca się zapoznać się z oboma stylami i powstrzymać się od religijności zamiast stylów ortodontycznych.

Więc podczas nauki na pewno skupiaj się na stylu, który ułatwia ci naukę, ale miej oko na drugiego, gdy jesteś przy nim i wszystko będzie dobrze.

Maximus Minimus
źródło
1
Nie jestem pewien, czy jest to domyślny formatator czy coś w ReSharper, ale kiedy użyłem C #, mieliśmy zestaw opcji, który ponownie sformatował kod w ramach odprawy. W ten sposób można sformatować kod w dowolny sposób podczas pracy z nim, ale zostanie on ponownie sformatowany do „standardu projektu” podczas odprawy. Myślę, że jedynym prawdziwym standardem formatowania, jaki mieliśmy, było użycie spacji zamiast tabulatorów.
TMN
1

Użyj Resharper, który pomoże zalecić sposoby ograniczenia zagnieżdżania. Przeczytaj także książkę Boba Martina Clean Code , która podkreśla, że ​​funkcja powinna robić tylko jedną rzecz, a zatem każda funkcja powinna mieć tylko pół tuzina linii, więc nie będziesz musiał się martwić tyloma poziomami zagnieżdżenia.

lorddev
źródło
1

W edytorze znajduje się dodatek, który może ci pomóc w: C # Zarys .

Dodatek rozszerza edytor VS20xx dla C #, dodając funkcje zwijania, rozwijania i wyróżniania zagnieżdżonych bloków kodu. Funkcje te pozwalają na łatwiejszą edycję i czytanie zagnieżdżonych treści bloków kodu, takich jak if, while itp.

Bez szans
źródło
0

Jeśli napiszesz swój kod w Visual Studio, istnieje również wtyczka, która pokazuje pionowe kropki między początkiem a końcem każdej budowanej struktury.

Ale ogólnie myślę, że minie trochę czasu, zanim przyzwyczaisz się do „Curly-Braces-Soup”. (Btw, naprawdę podoba mi się to wyrażenie. Brzmi trochę jak nazwa odcinka teorii The Big Bang)

mhr
źródło
0

Wcięcie mówi ci, gdzie jesteś, w obu stylach składni. Jeśli napiszesz program VB lub program C # w jednym wierszu, wkrótce nie będziesz w stanie powiedzieć, gdzie jesteś w zagnieżdżonej składni. Maszyna analizuje frazy kończące blok lub nawiasy klamrowe, ale ludzie potrzebują wcięcia.

Zwroty blokowe pochodzą z epoki kart perforowanych i taśmy papierowej, kiedy programowanie było znacznie mniej interaktywne i wizualne. Lub, naprawdę, wcale nie interaktywny. Trudno było wchodzić do programów, dlatego programiści potrzebowali kompilatorów, którzy byli bardzo sprytni w analizie składni i usuwaniu błędów.

W tej minionej epoce cykl edycji i kompilacji mógł polegać na przygotowaniu kart dziurkowanych za pomocą dziurkacza kart, a następnie ustawieniu się w oknie przesyłania zadań, w którym urzędnik wziął karty dziurkowane i przekazał je do maszyny. Później programista zbierał dane wyjściowe (wydrukowane na papierze) z innego okna. Gdyby program zawierał błędy, dane wyjściowe składałyby się tylko z diagnostyki kompilatora. Gdy czasy realizacji są długie, dodatkowy koszt pisania end ifzamiast po prostu )jest uzasadniony, jeśli pomaga to poprawić jakość diagnostyki, ponieważ programista musi poprawić jak najwięcej błędów w jednej iteracji, aby zmniejszyć straty czasu iteracje przez okno przesyłania zadania.

Kiedy brakuje zamykającego nawiasu klamrowego, trudno jest stwierdzić, który otwarty nawias klamrowy jest tym, który nie jest zamknięty. (Kompilator może przeanalizować wcięcie, aby odgadnąć odgadnięcie). Jeśli usuniesz nawias zamykający wewnątrz funkcji, wygląda na to, że cała reszta pliku jest częścią tej funkcji, co powoduje lawinę niepomyślnych komunikatów o błędach. Podczas gdy jeśli masz end functionskładnię, kompilator może wywnioskować, gdzie kończy się błędna funkcja, odzyskać i poprawnie przeanalizować kolejne funkcje, zapewniając dodatkową diagnostykę, jeśli taka ma znaczenie.

Gdy pracujesz w edytorze tekstów z rozpoznawaniem kodu, który automatycznie wcina i koloruje kod, na ekranie o wysokiej rozdzielczości, w którym widać sześćdziesiąt lub więcej wierszy, argumenty dla tego rodzaju niezdarnych języków nie mają już zastosowania. Możesz stopniowo edytować i odbudowywać programy tak szybko, że możesz poradzić sobie z jednym błędem naraz. Ponadto, widząc jednocześnie duże sekcje programu na ekranie i utrzymując odpowiednie wcięcie, możesz w pierwszej kolejności ograniczyć występowanie tego rodzaju błędów zagnieżdżania. Dobry programujący edytor tekstu będzie nawet oznaczał niektóre rodzaje błędów składniowych podczas pisania. Co więcej, istnieją składane edytory, które zwiną bloki programu na podstawie jego składni, dając „zarysowy” widok jego struktury.

Lisp od samego początku używał nawiasów i być może nieprzypadkowo hakerzy Lisp byli pionierami programowania jako interaktywnego doświadczenia, budując systemy przyjmujące programy w małych porcjach (wyrażeniach).

W rzeczywistości wcale nie potrzebujesz symboli końcowych, jak ilustruje język Python. Identyfikacja może być po prostu strukturą. Ludzie już używają wcięć, aby przeglądać strukturę kodu nawet w językach, w których maszyna polega na końcowych symbolach lub frazach.

Kaz
źródło
-1

Jeśli używasz IDE, po prostu naciśnij Crtl+ k+, Da IDE wykona resztę pracy.

Jagz W.
źródło
które zaćmienie IDE używa ctrl-shift-i do wcięcia i ctrl-shift-f do formatowania
maniak zapadkowy
sprawdź w studio wizualnym
Jagz W