Chcę wiedzieć, co jest uważane za lepszy sposób zwrotu, gdy mam if
oświadczenie.
Przykład 1:
public bool MyFunction()
{
// Get some string for this example
string myString = GetString();
if (myString == null)
{
return false;
}
else
{
myString = "Name " + myString;
// Do something more here...
return true;
}
}
Przykład 2:
public bool MyFunction()
{
// Get some string for this example
string myString = GetString();
if (myString == null)
{
return false;
}
myString = "Name " + myString;
// Do something more here...
return true;
}
Jak widać w obu przykładach, funkcja zwróci, true/false
ale czy warto umieścić else
instrukcję jak w pierwszym przykładzie, czy lepiej jej nie umieszczać?
else
.Odpowiedzi:
Przykład 2 jest znany jako blok ochronny. Lepiej jest odpowiednio wcześnie zwrócić / wyrzucić wyjątek, jeśli coś poszło nie tak (zły parametr lub nieprawidłowy stan). W normalnym przepływie logicznym lepiej jest użyć przykładu 1
źródło
Moim osobistym stylem jest używanie pojedynczego
if
dla bloków ochronnych iif
/else
w rzeczywistym kodzie przetwarzania metody.W tym przypadku używasz
myString == null
warunku ochronnego, więc chciałbym użyć jednegoif
wzorca.Rozważ kod, który jest nieco bardziej skomplikowany:
Przykład 1:
Przykład 2:
W przykładzie 1 zarówno strażnik, jak i reszta metody są w formie
if
/else
. Porównaj to z przykładem 2, w którym blok ochronny jest w pojedynczejif
formie, podczas gdy reszta metody używa formyif
/else
. Osobiście uważam przykład 2 za łatwiejszy do zrozumienia, podczas gdy przykład 1 wygląda na niechlujny i nadmiernie wcięty.Zauważ, że jest to wymyślony przykład i że możesz użyć
else if
instrukcji, aby go wyczyścić, ale chcę pokazać różnicę między blokami ochronnymi a rzeczywistym kodem przetwarzania funkcji.Przyzwoity kompilator i tak powinien wygenerować te same dane wyjściowe dla obu. Jedynym powodem użycia jednego lub drugiego jest osobiste preferencje lub dostosowanie się do stylu istniejącego kodu.
źródło
osobiście wolę drugą metodę. Wydaje mi się, że jest krótszy, ma mniej wcięć i jest łatwiejszy do odczytania.
źródło
Moja osobista praktyka jest następująca:
Na przykład:
Na przykład:
Reguła „jednego wyjścia” pomaga również wtedy, gdy obliczenia wymagają zewnętrznych zasobów, które musisz zwolnić, lub stwierdza, że musisz zresetować przed opuszczeniem funkcji; czasami są one dodawane później podczas programowania. Przy wielu wyjściach w algorytmie znacznie trudniej jest poprawnie rozszerzyć wszystkie gałęzie. (A jeśli mogą wystąpić wyjątki, wydanie / reset należy również umieścić w bloku końcowym, aby uniknąć skutków ubocznych w rzadkich wyjątkowych przypadkach ...).
Twoja sprawa wydaje się należeć do kategorii „szybkie wyjście przed prawdziwą pracą” i napisałbym ją tak, jak w twojej wersji z Przykładu 2.
źródło
Wolę stosować bloki ochronne tam, gdzie mogę, z dwóch powodów:
Ogólnie rzecz biorąc wolę widzieć metody, w których podstawowa funkcjonalność metody jest jasna i minimalna. Bloki ochronne pomagają wizualnie to zrobić.
źródło
Podoba mi się podejście „Fall Through”:
Akcja ma określony warunek, wszystko inne to tylko domyślny powrót „przechodzący”.
źródło
Jeśli mam jeden warunek if, nie spędziłbym zbyt wiele czasu na rozmyślaniu o tym stylu. Ale jeśli mam wiele warunków ochrony, wolałbym style2
Zrób to. Załóżmy, że testy są złożone i naprawdę nie chcesz ich wiązać w jeden warunek jeśli-ORed, aby uniknąć złożoności:
przeciw
Oto dwie główne zalety
Oddzielasz kod w jednej funkcji na dwa wizualnie logiczne bloki: górny blok walidacji (warunki ochrony) i dolny blok uruchamialnego kodu.
Jeśli musisz dodać / usunąć jeden warunek, zmniejszasz swoje szanse na zepsucie całej drabiny if-elseif-else.
Kolejną niewielką zaletą jest to, że masz o jeden zestaw mniejszej liczby aparatów ortodontycznych.
źródło
Powinno to być kwestią, która brzmi lepiej.
wyraża się wtedy lepiej
w przypadku małych metod nie będzie to wielka różnica, ale w przypadku większych metod złożonych może być trudniejszy do zrozumienia, ponieważ nie będzie odpowiednio oddzielny
źródło
do something
zawierają zwrot. Jeśli nie, drugi kod zostanie wykonanydo somethingelse
niezależnie odif
predykatu, który nie działa w taki sposób, jak pierwszy blok.Uważam przykład 1 za irytujący, ponieważ brakująca instrukcja return na końcu funkcji zwracającej wartość natychmiast wyzwala flagę „Czekaj, coś jest nie tak”. W takich przypadkach wybrałbym przykład 2.
Jednak zwykle jest więcej zaangażowanych, w zależności od celu funkcji, np. Obsługa błędów, rejestrowanie itp. Więc jestem z odpowiedzią Loranda Kedvesa na ten temat i zwykle mam jeden punkt wyjścia na końcu, nawet kosztem dodatkowej zmiennej flagi. W większości przypadków ułatwia konserwację i późniejsze rozszerzenia.
źródło
Kiedy twoja
if
instrukcja zawsze zwraca, nie ma powodu, aby używać innego dla pozostałego kodu w funkcji. Takie postępowanie dodaje dodatkowe linie i dodatkowe wcięcia. Dodanie niepotrzebnego kodu utrudnia czytanie i, jak wiadomo, czytanie kodu jest trudne .źródło
W twoim przykładzie
else
jest to oczywiście niepotrzebne, ALE ...Podczas szybkiego przeglądania linii kodu oczy zwykle patrzą na nawiasy klamrowe i wgłębienia, aby uzyskać pojęcie o przepływie kodu; zanim faktycznie osiądą na samym kodzie. Dlatego pisanie
else
i wstawianie nawiasów klamrowych i wcięć wokół drugiego bloku kodu sprawia, że czytelnik szybciej widzi, że jest to sytuacja „A lub B”, w której jeden lub drugi blok będzie działał. Oznacza to, że czytelnik widzi nawiasy klamrowe i wgłębienia, ZANIM zobacząreturn
po inicjałachif
.Moim zdaniem jest to jeden z tych rzadkich przypadków, w których dodanie czegoś nadmiarowego do kodu sprawia, że jest on WIĘCEJ czytelny, a nie mniej.
źródło
Wolałbym przykład 2, ponieważ natychmiast widać, że funkcja coś zwraca . Ale nawet więcej, wolałbym wrócić z jednego miejsca, takiego jak to:
W tym stylu mogę:
Zobacz od razu, że coś zwracam.
Złap wynik każdego wywołania funkcji za pomocą tylko jednego punktu przerwania.
źródło
Mój osobisty styl zwykle odchodzi
Używanie skomentowanych
else
instrukcji w celu wskazania przebiegu programu i zachowania jego czytelności, ale unikanie masywnych wcięć, które mogą łatwo dotrzeć na ekran, jeśli zaangażowanych jest wiele kroków.źródło
else
dołączonymi klauzulami, wybrałbym to drugie, ponieważ kompilator skutecznie je również zignoruje. Choć zależałoby to od tego, byelse if
nie zwiększać wcięcia więcej niż oryginał - jeśli tak , zgodziłbym się z tobą. Ale moim wyborem byłobyelse
całkowite porzucenie, gdyby ten styl był dozwolony.Napisałbym
Wolę ten styl, ponieważ najbardziej oczywiste jest, że wrócę
userName != null
.źródło
myString
wyniku. Właśnie kopiuję funkcję z OP. Zamienię to na coś, co wygląda bardziej realistycznie. Test boolowski jest trywialny i nie powinien być problemem.Moją ogólną zasadą jest albo wykonanie zwrotu „bez” (głównie w przypadku błędów), albo wykonanie pełnego łańcucha „jeśli-inaczej-jeśli” przy wszystkich możliwościach.
W ten sposób unikam zbytniej fantazji przy rozgałęzianiu, ponieważ za każdym razem, gdy to robię, koduję logikę aplikacji w moich ifs, co utrudnia ich utrzymanie (na przykład, jeśli wyliczenie OK lub ERR i piszę wszystkie moje ifs korzystając z faktu, że! OK <-> ERR staje się problemem w tyłku, aby dodać trzecią opcję do wyliczenia następnym razem).
W twoim przypadku użyłbym na przykład zwykłego, jeśli „return if null” z pewnością będzie wspólnym wzorcem i jest mało prawdopodobne, że będziesz musiał się martwić o trzecią możliwość (oprócz null / not null) w przyszłości.
Jeśli jednak test był czymś, co sprawdziło bardziej szczegółową kontrolę danych, popełniłbym błąd w kierunku pełnego „jeśli-inaczej”.
źródło
Myślę, że w Przykładzie 2 jest wiele pułapek, które mogą prowadzić do niezamierzonego kodu. Pierwszy punkt zainteresowania jest tutaj oparty na logice otaczającej zmienną „myString”. Dlatego, mówiąc wprost, wszystkie testy warunków powinny odbywać się w bloku kodu uwzględniającym znaną i domyślną / nieznaną logikę.
Co jeśli później kod został przypadkowo wprowadzony do przykładu 2, który znacząco zmienił wynik:
Myślę, że
else
blok bezpośrednio po sprawdzeniu wartości zerowej spowodowałby, że programiści, którzy dodali ulepszenia do przyszłych wersji, dodają całą logikę razem, ponieważ teraz mamy ciąg logiki, który był niezamierzony dla oryginalnej reguły (zwracany, jeśli wartość jest zero).Wierzę w to mocno niektórym z wytycznych C # na Codeplex (link do tego tutaj: http://csharpguidelines.codeplex.com/ ), które zawierają następujące informacje:
„Dodaj opisowy komentarz, jeśli domyślny blok (inny) ma być pusty. Ponadto, jeśli ten blok nie zostanie osiągnięty, wyrzuć wyjątek InvalidOperationException, aby wykryć przyszłe zmiany, które mogą wystąpić w istniejących przypadkach. Zapewnia to lepszy kod, ponieważ wszystkie ścieżki, które kod może przemierzać, zostały przemyślane. ”
Myślę, że dobrą praktyką programistyczną jest stosowanie takich bloków logicznych, aby zawsze dodawać blok domyślny (jeśli-inaczej, case: default), aby jawnie uwzględniać wszystkie ścieżki kodu i nie pozostawiać kodu otwartego na niezamierzone konsekwencje logiczne.
źródło
myString
wartością, a kod następujący poif
bloku jest logiką wynikową, jeśli łańcuch! = Null. Dlatego, ponieważ nacisk kładziony jest na dodatkową logikę manipulującą tą konkretną wartością, myślę, że powinna ona być zamknięta welse
bloku. W przeciwnym razie otwiera to potencjał, ponieważ pokazałem, że mimowolnie rozdzielam logikę i wprowadzam niezamierzone konsekwencje. Może się to nie zdarzać cały czas, ale tak naprawdę było to pytanie oparte na opinii najlepszych praktyk .