Spis treści:

Kategoria:C#


Nazwy funkcji, metod i właściwości w C#

Znaczenie nazw w programowaniu

Zaczaił się wilk na czerwonego kapturka. Gdy dziewczynka nadeszła wilk rzucił się na nią.... i ją zgwałcił. Spodobało mu się, to ją zgwałcił jeszcze raz, i jeszcze raz.
Po kilku następnych razach pada wycieńczony obok Kapturka. Czerwony Kapturek unosi się na łokciach i spokojnie pyta:
- Wilku, masz ty chociaż zaświadczenie, że nie jesteś chory na aids?
- Pewnie, że mam!
- No to możesz je podrzeć...

Każde szanujące się opracowanie na temat tworzenia dobrego kodu, w tym właściwego nazewnictwa zmiennych, klas, metod i właściwości powtarza jak mantrę stwierdzenie: obiekty powinny być samoopisujące się. Takie dobre nazwy prowadzą w konsekwencji do samoopisującego się kodu. Wiadomo przecież, że metoda Save w klasie typu Order będzie zapisywała zamówienie, wiadomo, że właściwość FirstName w klasie Person będzie przechowywała imię jakiejś osoby. Z drugiej strony, jeżeli w klasie znajdują się metody Convert1, Convert2 i Convert3 to zdrowy programista zrobi jedno - usiądzie i zapłacze. O ile już nie siedział i jeszcze nie płakał. Idąc dalej - jeżeli w klasie znajdują się właściwości Type, State, StateType to nawet najbardziej doświadczony programista nie odgadnie, co miał na myśli autor dzieła i co w każdym z pól się znajduje. Jeżeli metody mają kilkanaście parametrów, jeżeli te parametry nazywają się p1, p2, p3, p4 i tak dalej - konia z rzędem temu, kto zrozumie kod bez wnikania w szczegóły implementacyjne. To nie są moje wymysły, bo każdą z tych konstrukcji już widziałem w żywym kodzie. Aby poprawić ten stan rzeczy kierownik przedstawia swój własny, oryginalny pomysł: skoro kod jest nieczytelny, piszmy komentarze! Pojawia się niezadowolenie, bo te komentarze ktoś przecież musi pisać. Ale nie to jest najgorsze. Po jakimś czasie jakiś programista przerabia metodę i zmienia jej argumenty. Być może także zwracaną wartość. Co robi z komentarzami? Nic! Komentarze jakie były, takie są. Zamiast podpowiadać, wprowadzają chaos. Powiem więcej, wymaganie komentarzy i bezmyślne wstawianie bezsensownych regułek potrafi doprowadzić do furii niejednego rozsądnego człowieka. Po co pisać, że metoda GetPersonById(int id) pobiera osobę na podstawie identyfikatora i że parametr id to identyfikator? Po co pisać, że właściwość Email tej osoby pozwala na ustawienie lub pobranie e-maila dla... tej osoby. A co niby miałaby robić? Kończy się to tym, że komentarze są bezmyślnie kopiowane i tylko niepotrzebnie zaśmiecają kod. Dużo można o tym pisać, ale nie taki jest mój plan. Ten wpis będzie nieco luźniejszy niż inne. Zainteresowanych tematyką odsyłam do odpowiednich pozycji książkowychCode Complete: A Practical Handbook of Software Construction, Wydanie II, 2004The Pragmatic Programmer: From Journeyman to Master, 1999.

Luźne podejście do nazw

Temat nazewnictwa obiektów języka C# jest dobrym przyczółkiem do zainteresowania ciekawszymi elementami języka. Mało kto wie, że nazwy, zwane wewnętrznie identyfikatorami, mogą przyjmować postać dowolnych łańcuchów znakowych dających się zapisać w postaci znaków Unicode. Pozwolę sobie na zacytowanie fragmentu specyfikacji Common Language Infrastructure (CLI):

II.5.3 Identifiers

Identifiers are used to name entities. Simple identifiers are equivalent to an ID. However, the ILAsm syntax allows the use of any identifier that can be formed using the Unicode character set [...]

Język C#, zbudowany na bazie tej infrastruktury wprowadza trochę ograniczeń. Trzeba jednak wiedzieć, jakie są konsekwencje stosowanie różnych symboli. Sama specyfikacja podaje przykłady różnych kontrowersyjnych z punktu widzenia czystości kodu nazw:

The following are simple identifiers:
A Test $Test @Foo? ?_X_ MyType`1
The following are identifiers in single quotes:
'Weird Identifier' 'Odd\102Char' 'Embedded\nReturn'
The following are dotted names:
System.Console 'My Project'.'My Component'.'My Name' System.IComparable`1

To, że coś można zrobić, nie oznacza wcale, że powinniśmy z tego korzystać. Nie oznacza też, że nie można się tym pobawić.

Wykorzystanie niestandardowej nazwy w klasie C#

Wiem, że specyfikacje i opisy nie działają tak dobrze jak kod. Pokażę zatem przykład:

public class Equation
{
    public double A { get; set; }
    public double B { get; set; }
    public double C { get; set; }
    public double Δ { get; set; }
    public Equation(double a, double b, double c)
    {
        this.A = a;
        this.B = b;
        this.C = c;
        this.Δ = b * b - 4 * a * c;
    }
    (...)
}

Powyższa klasa mogłaby być odpowiedzialna za rozwiązywanie równań kwadratowych. Przyjęło się, że wyznacznik takiego równania oznaczamy grecką literą delta (Δ). Tak też zrobiłem w kodzie. Pozwala to na zwięzły zapis w postaci:

var eq = new Equation(1, 6, 4);
var delta = eq.Δ;

To nie wszystko! Można się jeszcze bardziej zagłębić i obliczać w klasie miejsca zerowe funkcji. Popatrzmy na poniższy listing:

public double X1
{
    get
    {
        if (A != 0)
            return (-B - Math.Sqrt(Δ)) / (2 * A);
        else
            throw new ArgumentException("A = 0");
    }
}

To, że można tego symbolu użyć to nic. Visual Studio będzie go poprawnie rozpoznawał i nawet pokaże go w oknach IntelliSense. Przejdźmy do innego przykładu.

Nazwa obiektu w postaci szesnastkowej

Łańcuchy w C# można zapisywać na różne sposoby. Gdy znak jest reprezentowany przez klawisz na klawiaturze, wtedy nie ma problemu. Gdy takiej reprezentacji nie ma, możemy:

  • skopiować pożądany znak z innego źródła,
  • wprowadzić go w postaci szesnastkowejPostać szesnastkowa w rzeczywistości reprezentuje numer znaku w tablicy Unicode. poprzedzając znakami \u.

Symbol w postaci szesnastkowej może być również podany w nazwie obiektu. Popatrzmy na przykład:

public class Pipe
{
    public double \u03A6 { get; private set; }
    public double R { get { return Φ / 2; } }
    public Pipe(int \u03A6)
    {
        this.Φ = \u03A6;
    }
}

Symbol \u03A6 to szesnastkowa postać znaku Φ. Co ciekawe, gdy zechcemy utworzyć obiekt, IntelliSense pokaże nam parametr konstruktora w postaci symbolu Φ. Czy pokazane klasy mają praktyczne zastosowanie?

Nazewnictwo w praktyce

Napisałem na początku, że wpis ma charakter ciekawostki. Zależało mi na pokazaniu pewnej, pozornie ukrytej, właściwości języka. Pozornie ukrytej, bo dokładnie zdefiniowanej i opisanej w specyfikacji. W tej specyfikacji znajduje się dużo innych ciekawych konstrukcji. W zdecydowanej większości przypadków nie będą one wykorzystywane w projektach. Nie oznacza to jednak, że nie są potrzebne. Im głębiej zanurzymy się w dokument, tym łatwiej przyjdzie nam zrozumienie innych elementów języka. Tym łatwiej przyjdzie nam integracja z innymi językami na platformie .NET i wykorzystanie wszystkich możliwości które w nich leżą. Język C# i jego kompilator to tylko dodatkowa warstwa położona na fundamencie CLI. To tak jak z językiem maszynowym komputera, asemblerem. Można programować w C, C++ i nigdy nie napisać żadnej linijki w kodzie maszynowym. Powiem więcej - można z tym dobrze żyć. Ale, ten bliski kontakt z maszyną, zrozumienie niskopoziomowych mechanizmów, dotknięcie sprzętu pojedynczą instrukcją - to wrażenia niezapomniane. Ten wpis miał być zachętą. Zachętą do zgłębienia tajemnic kodu pośredniego na platformie .NET. Nie zamierzam zdradzać szczegółów, ale uchylę rąbka tajemnicy. Są pewne mechanizmy dostępne w VB.NET, a niedostępne w C#. Są przydatne mechanizmy dostępne w kodzie pośrednim i także niedostępne w C#. Jakie? O tym może innym razem.

Kategoria:C#

, 2014-01-31

Brak komentarzy - bądź pierwszy

Dodaj komentarz
Wyślij
Ostatnie komentarze
Dzieki za rozjasnienie zagadnienia upsert. wlasnie sie ucze programowania :).
Co się stanie gdy spróbuję wyszukać:
SELECT * FROM NV_Airport WHERE Code='SVO'
SELECT * FROM V_Airport WHERE Code=N'SVO'
(odwrotnie są te N-ki)
Będzie konwersja czy nie znajdzie żadnego rekordu?