Dzisiaj zamieszczam drugi post z serii Top 11. Tym razem - krótkie miniporady zwiększające czytelność i polepszające organizację kodu. Wszystkie bezdyskusyjnie stosowane przeze mnie i zdecydowanie sprawdzone. Zdaję sobie sprawę, że nie napiszę nic nadzwyczajnie odkrywczego, jednak jeśli chociaż kilku osobom pojawi się w głowie chmurka z napisem "faktycznie, można to tak zrobić" - to już będzie mój wielki sukces:).
Oczywiście to co zaprezentuję to najniższy poziom "usprawnień" - na tymże poziomie przykładowych pojęć "wzorce projektowe" czy "polimorfizm" nawet się nie bierze pod uwagę. I o to chodzi, bardziej skomplikowane rzeczy zasługują na osobne, tylko im poświęcone posty.
Zatem po kolei:
1) Regiony
Gdy jakiś czas temu zostałem brutalnie wrzucony w świat Javy - brak regionów uderzył we mnie z całą mocą. WTF? Jak się bez tego połapać w kodzie? Chwała pomysłodawcy regionów. A tym, którzy mogą ich używać, a nie używają - na pohybel! Dla zupełnie zielonych: gdy jakąś część kodu otoczymy dyrektywami
1: #region [NazwaRegionu]
2: #endregion
to Visual ślicznie zwinie nam całość w jedną linijkę.
2) Usings
Piszmy DateTime zamiast System.DateTime, piszmy StringBuilder zamiast System.Text.StringBuilder, i tak dalej... A konieczne do zaimportowania przestrzenie nazw umieszczajmy na samej górze pliku, najlepiej w zwiniętej sekcji "usings".
Kompilatorowi nie robi to różnicy, a czytającemu kod - jak najbardziej.
Oczywiście czasami są wyjątki od tej reguły, ale tak to już jest z regułami...
3) Zakomentowany kod jest be!
Nie komentujmy kodu - usuwajmy go! Zakomentowany kod może mieć sens przez kwadrans, może dzień, może dwa. Ale po dłużej chwili i tak nie będzie nadawał się do użytku. Nawet jeśli zawarta w nim funkcjonalność za X czasu okaże się jednak potrzebna, zdecydowanie lepiej jest napisać ją od nowa niż odkomentować stary kod, zagłębiać się w jego sens, bezmyślnie poprawiać metodą "kompiluje się, znaczy działa" a potem spędzać długie godziny nad szukaniem błędów, których w takich sytuacjach ciężko uniknąć.
Poza tym - mamy przecież repozytorium kodu, za pomocą którego bez problemu dostaniemy się do każdej napisanej, zedytowanej i usuniętej linijki.
4) Nazewnictwo
Porada bardzo krótka, jednak bardzo ważna: stosujmy nazwy tak, jak zalecane jest w dokumencie ".NET Framework General Reference - Naming Guidelines". Bez zbędnych wynalazków, bez super-wymyślnych i mega-zrozumiałych własnych konwencji... Ten dokument to po prostu przyjęty i zaakceptowany standard (oczywiście w świecie .NET - smutek wynikły z nazywania metod z małej litery czy zostawiania nawiasów klamrowych w tej samej linii zostawmy Javowcom:)).
5) Operatory ?? i ?:
Pierwszy operator pozwala wyeliminować zbędne porównania do null:
1:
2: if (p == null)
3: return new object();
4: else
5: return p;
6:
7:
8: return p ?? new object();
Drugi operator z kolei skraca proste ify tak, by logicznie można je było zapisać w postaci jednej operacji:
1:
2: if (warunek)
3: return 1;
4: else
5: return 0;
6:
7:
8: return (warunek) ? 1 : 0;
6) Globals.cs, Common.cs, Shared.cs i inne SodomaIGomora.cs
Nie twórzmy plików, których przeznaczeniem jest przechowywanie wszystkiego, co nie pasuje nigdzie indziej! Ileż razy widzieliśmy klasę Globals operującą na datach, wykonującą konwersje między typami, pokazującą wiadomości w MessageBoxach...? Tylko yerba-mate nie parzy.
Oczywiście nie można uniknąć w systemie różnego rodzaju metod pomocniczych bez wielokrotnego ich implementowania (co jest jeszcze gorsze niż Śmietnik.cs). W takiej sytuacji starajmy się chociaż grupować podobne w operacje w specjalne klasy, na przykład w katalogu Utils niech znajdą się pliki DateTimeUtils.cs, MessageUtils.cs i tak dalej... Nadal nie wygląda to ślicznie jak Doda "The Bright Silicon Doll", ale nie potrafię wymyślić lepszego sposobu. Może jakieś sugestie od czytelników?
7) Enum (typy wyliczeniowe - nie mylić z enumeratorem)
Tworząc enum mamy na myśli coś konkretnego - chcemy nadać jakiejś klasie pewną cechę, której wartości mają zawierać się w ściśle określonym zakresie. Niejednokrotnie można spotkać się z "enumami" porozrzucanymi po całym projekcie lub zgrupowanymi w pliku Enums.cs. A dlaczego? Przecież zwykle enum jest ściśle powiązany z jedną klasą lub jedną hierarchią klas. Dlatego definiujmy je wewnątrz owej klasy z modyfikatorem public, na przykład tak:
1: class Person
2: {
3: public enum PersonGender
4: {
5: Male,
6: Female
7: }
8:
9: public PersonGender Gender { get; set; }
10: }
8) GenerateMember = false
Kontrolki w Windows Forms mają właściwość GenerateMember. Domyślnie jej wartość to true, jednak zachęcam do zmiany na false wszędzie, gdzie to tylko możliwe.
Co to zmieni? Deklaracja kontrolki zostanie przeniesiona z ciała klasy do wnętrza metody InitializeComponent, dzięki czemu bezpośredni dostęp do niej z kodu stanie się niemożliwy.
Moim zdaniem taka powinna być domyślna wartość tej właściwości (albo powinna być chociaż możliwość zmiany domyślnej wartości z poziomu VS, czego nie udało mi się znaleźć). Dlaczego? Wówczas bardziej popularne byłoby intensywne wykorzystanie DataBinding (klasy BindingSource, BindingContext, CurrencyManager...) zamiast ręcznej synchronizacji danych <-> UI czy odwoływanie się do parametru sender z obsługi zdarzeń zamiast bezpośrednio do konkretnej kontrolki.
Oczywiście pewnych problemów nie przeskoczymy, i wtedy nie obejdzie się bez posiadania danego elementu UI w postaci pola klasy, ale takie sytuacje to wyjątki.
9) Aliasy C#
Język C# posiada aliasy dla większości (wszystkich?) typów prymitywnych oraz kilku nieprymitywnych. Korzystajmy z nich: używajmy int zamiast System.Int32, float zamiast System.Single, string zamiast System.String czy object zamiast System.Object - to dla naszej własnej wygody.
10) Warunki kończące metodę
Jeżeli wykonanie czynności zawartych metodzie zależy od pewnych warunków - sprawdźmy ich negację i wyjdźmy z metody, jeśli zachodzi taka potrzeba. Unikniemy pociętego, "potabowanego", nieczytelnego kodu. Dla zobrazowania mały przykład:
1:
2: metoda()
3: {
4: if (warunek)
5: {
6: instrukcja1();
7: instrukcja2();
8: }
9: }
10:
11:
12: metoda()
13: {
14:
15: if (warunek == false)
16: return;
17:
18: instrukcja1();
19: instrukcja2();
20: }
11) var...
To słowo kluczowe dodane w C# 3.0 jest dla programisty kuszące niczym dla Adama jabłko nadgryzione przez nagą Ewę. Ba, Resharper 4 domyślnie bardzo usilnie namawia nas nawet do stosowania go przy deklaracji KAŻDEJ zmiennej lokalnej! Ja bym był jednak ostrożny - i zalecam jego użycie tylko w scenariuszach związanych z LINQ i typami anonimowymi oraz w definicji pętli foreach.
To 11 banalnych porad, które moim zdaniem mogą dość znacząco wpłynąć na przejrzystość pisanego kodu. Bardzo chętnie poznam Wasze opinie o przedstawionych poglądach i poznam więcej podobnych zasad. Co robicie, aby Wasz kod był czytelny i ładny?