Konwencje nazewnictwa w C#

Wprowadzenie

Dobre nazewnictwo to jeden z najważniejszych aspektów pisania czytelnego i profesjonalnego kodu. W C# istnieją ustalone konwencje, które pomagają programistom na całym świecie rozumieć kod napisany przez innych.

Dlaczego konwencje są ważne?

  • Czytelność kodu – inni programiści (i Ty w przyszłości) łatwiej zrozumieją kod
  • Spójność – cały zespół pisze kod w podobny sposób
  • Profesjonalizm – kod wygląda profesjonalnie i jest zgodny ze standardami branżowymi
  • Łatwiejsze utrzymanie – znajdowanie i modyfikowanie kodu staje się prostsze
  • Zgodność z ekosystemem .NET – Twój kod integuje się lepiej z bibliotekami Microsoft

Zasady techniczne (obowiązkowe)

Zanim przejdziemy do konwencji, przypomijmy sobie zasady techniczne, których musi przestrzegać każdy identyfikator w C#:

✅ Dozwolone znaki:

  • Litery (a-z, A-Z, w tym znaki Unicode)
  • Cyfry (0-9) – ale nie na początku
  • Znak podkreślenia (_)

❌ Niedozwolone:

  • Spacje
  • Znaki specjalne (@, #, %, &, *, itd.)
  • Słowa kluczowe (chyba że poprzedzone @)
  • Zaczynanie od cyfry
// ✅ Prawidłowe nazwy
string imieUzytkownika;
int liczba1;
bool _czyWlaczony;
double współczynnikAlfa;  // Unicode dozwolony

// ❌ Nieprawidłowe nazwy
// string 1nazwa;         // Zaczyna się od cyfry
// int my-variable;       // Zawiera myślnik
// bool class;            // Słowo kluczowe
// string imię użytkownika; // Zawiera spację

// ✅ Obejście dla słów kluczowych (rzadko używane)
string @class = "nazwa klasy";  // @ pozwala użyć słowa kluczowego

Wielkość liter ma znaczenie!

int mojWiek = 25;
int MojWiek = 30;     // To jest inna zmienna!
int MOJWIEK = 35;     // To też inna zmienna!

Konwencje nazewnictwa w C#

1. Zmienne lokalne – camelCase

Reguła: Pierwsza litera mała, każde kolejne słowo zaczyna się wielką literą.

public void PrzykladMetody()
{
    // ✅ Prawidłowe nazwy zmiennych lokalnych
    int wiekUzytkownika = 25;
    string imieNazwisko = "Jan Kowalski";
    bool czyJestAktywny = true;
    double sredniOcen = 4.75;
    DateTime dataUrodzenia = new DateTime(1995, 5, 15);
    
    // Dla zmiennych jednoliterowych (w pętlach)
    for (int i = 0; i < 10; i++)
    {
        // 'i', 'j', 'k' są akceptowalne w pętlach
    }
    
    // ❌ Złe nazwy
    int WiekUzytkownika;     // Zaczyna się wielką literą
    string imie_nazwisko;    // Używa podkreślenia
    bool CzyJestAktywny;     // PascalCase zamiast camelCase
}

Parametry metod – camelCase

// ✅ Prawidłowe nazwy parametrów
public void ObluczSume(int pierwszaLiczba, int drugaLiczba)
{
    // kod metody
}

public string FormatujDane(string imie, string nazwisko, int wiek)
{
    return $"{imie} {nazwisko}, {wiek} lat";
}

// Parametry z modyfikatorami
public void PrzetworzDane(ref int wartoscWejsciowa, out string wynik)
{
    // kod
}

3. Pola prywatne – _camelCase lub camelCase

Microsoft zaleca dwa podejścia:

Podejście 1: _camelCase (zalecane przez Microsoft)

public class Osoba
{
    // ✅ Pola prywatne z podkreśleniem
    private string _imie;
    private int _wiek;
    private bool _czyAktywny;
    private DateTime _dataRejestracji;
    
    public string Imie 
    { 
        get { return _imie; } 
        set { _imie = value; } 
    }
}

Podejście 2: camelCase (również akceptowalne)

public class Osoba
{
    // ✅ Pola prywatne bez podkreślenia
    private string imie;
    private int wiek;
    private bool czyAktywny;
    
    public string Imie 
    { 
        get { return imie; } 
        set { imie = value; } 
    }
}

4. Właściwości (Properties) – PascalCase

public class Produkt
{
    // ✅ Właściwości publiczne - PascalCase
    public string Nazwa { get; set; }
    public decimal Cena { get; set; }
    public bool CzyDostepny { get; set; }
    public DateTime DataDodania { get; set; }
    public int IloscNaMagazynie { get; set; }
    
    // Właściwości tylko do odczytu
    public string PelnyOpis => $"{Nazwa} - {Cena:C}";
    
    // Właściwości prywatne też PascalCase
    private string KodWewnetrzny { get; set; }
}

5. Metody – PascalCase

public class Kalkulator
{
    // ✅ Metody publiczne - PascalCase
    public int DodajLiczby(int a, int b)
    {
        return a + b;
    }
    
    public double ObliczSrednia(params double[] liczby)
    {
        return liczby.Average();
    }
    
    public void WyswietlWynik(double wynik)
    {
        Console.WriteLine($"Wynik: {wynik}");
    }
    
    // Metody prywatne też PascalCase
    private bool CzyLiczbaParzysta(int liczba)
    {
        return liczba % 2 == 0;
    }
    
    // Metody asynchroniczne - często z końcówką Async
    public async Task<string> PobierzDaneAsync()
    {
        // kod asynchroniczny
        return await Task.FromResult("dane");
    }
}

6. Klasy – PascalCase

// ✅ Nazwy klas - PascalCase
public class Uzytkownik { }
public class KalkulatorNaukowy { }
public class MenedzerBazyDanych { }
public class PrzetwarzaczXML { }

// Klasy abstrakcyjne
public abstract class BazowaKlasaUzytkownika { }

// Klasy statyczne
public static class NarzedziaMatematyczne { }

// Klasy częściowe (partial)
public partial class DuzyFormularz { }

7. Interfejsy – IPascalCase

Reguła: Zawsze zaczynają się od litery „I” następnie PascalCase.

// ✅ Interfejsy - zawsze z przedrostkiem I
public interface IUzytkownik
{
    string Imie { get; set; }
    void Zaloguj();
}

public interface IKalkulator
{
    int Dodaj(int a, int b);
    int Odejmij(int a, int b);
}

public interface IDisposable  // Interfejs z .NET
{
    void Dispose();
}

// Interfejsy generyczne
public interface IRepozytorium<T>
{
    void Dodaj(T element);
    T Pobierz(int id);
}

8. Stałe – dwa podejścia

Podejście 1: PascalCase (zalecane przez Microsoft)

public class Stale
{
    // ✅ Stałe publiczne - PascalCase
    public const double Pi = 3.14159265359;
    public const string DefaultPassword = "Password123";
    public const int MaxUsers = 1000;
    
    // Stałe prywatne
    private const string ConnectionString = "Server=...";
}

Podejście 2: UPPER_CASE (tradycyjne)

public class Stale
{
    // ✅ Również akceptowalne - UPPER_CASE
    public const double PI = 3.14159265359;
    public const string DEFAULT_PASSWORD = "Password123";
    public const int MAX_USERS = 1000;
}

9. Enumeracje – PascalCase

// ✅ Enumy i ich wartości - PascalCase
public enum StatusZamowienia
{
    Nowe,
    WTrakciePrzetwarzania,
    Wyslane,
    Dostarczone,
    Anulowane
}

public enum DniTygodnia
{
    Poniedzialek = 1,
    Wtorek = 2,
    Sroda = 3,
    Czwartek = 4,
    Piatek = 5,
    Sobota = 6,
    Niedziela = 7
}

// Użycie
StatusZamowienia status = StatusZamowienia.WTrakciePrzetwarzania;

Tabela podsumowująca

Element koduKonwencjaPrzykład
Zmienne lokalnecamelCaseint wiekUzytkownika;
Parametry metodcamelCasevoid Metoda(string nazwaParametru)
Pola prywatne_camelCaseprivate string _imie;
WłaściwościPascalCasepublic string Imie { get; set; }
MetodyPascalCasepublic void ObliczSume()
KlasyPascalCasepublic class MojaKlasa
InterfejsyIPascalCasepublic interface IUzytkownik
StałePascalCasepublic const int MaxValue = 100;
EnumeracjePascalCasepublic enum Status { Aktywny }
NamespacesPascalCasenamespace MojaFirma.Aplikacja;

Specjalne przypadki i wyjątki

1. Akronimy i skróty

// ✅ Krótkie akronimy (2-3 litery) - wszystkie wielkie
public class XMLParser { }
public string URLAdres { get; set; }
public void ParseHTML() { }

// ✅ Długie akronimy - jak normalne słowa
public class HttpClient { }  // nie HTTPClient
public string SqlConnection { get; set; }  // nie SQLConnection

// W camelCase
string xmlContent;    // nie xMLContent
string httpResponse;  // nie hTTPResponse

2. Metody event handler

// ✅ Obsługa zdarzeń - często z końcówką EventHandler
private void Button_Click(object sender, EventArgs e) { }
private void Timer_Elapsed(object sender, ElapsedEventArgs e) { }
private void Window_Loaded(object sender, RoutedEventArgs e) { }

// Lub bez underscore
private void ButtonClickHandler(object sender, EventArgs e) { }

3. Zmienne w pętlach

// ✅ Pojedyncze litery w pętlach są OK
for (int i = 0; i < 10; i++)
{
    for (int j = 0; j < 5; j++)
    {
        // i, j, k są akceptowalne w krótkych pętlach
    }
}

// ✅ Ale dla większej czytelności lepiej używać opisowych nazw
for (int indeksWiersza = 0; indeksWiersza < wiersze; indeksWiersza++)
{
    for (int indeksKolumny = 0; indeksKolumny < kolumny; indeksKolumny++)
    {
        // bardziej czytelne w złożonych pętlach
    }
}

4. Zmienne tymczasowe

// ✅ W krótkich metodach można używać krótkich nazw
public string FormatujImie(string firstName, string lastName)
{
    var temp = firstName?.Trim();  // temp jest OK w krótkim zasięgu
    return $"{temp} {lastName}";
}

// ✅ Ale lepiej używać opisowych nazw
public string FormatujImie(string firstName, string lastName)
{
    var oczyszczoneImie = firstName?.Trim();
    return $"{oczyszczoneImie} {lastName}";
}

Najlepsze praktyki

1. Używaj opisowych nazw

// ❌ Złe - niejasne nazwy
int d = 30;
string s = "Jan";
bool f = true;

// ✅ Dobre - opisowe nazwy  
int dniDoWyplaty = 30;
string imieUzytkownika = "Jan";
bool czyUzytkownikAktywny = true;

2. Unikaj skrótów (chyba że są powszechnie znane)

// ❌ Niejasne skróty
string usr = "Jan";
int cnt = 10;
bool flg = true;

// ✅ Pełne nazwy
string uzytkownik = "Jan";
int licznik = 10;
bool flaga = true;

// ✅ Powszechnie znane skróty są OK
int id = 123;           // ID jest powszechnie zrozumiałe
string url = "http://..."; // URL też
double min = 5.0;       // min/max są jasne
double max = 10.0;

3. Używaj pozytywnych nazw dla bool

// ❌ Negatywne nazwy - mylące
bool nieAktywny = false;  // Podwójne zaprzeczenie!
if (!nieAktywny) { }      // Czy użytkownik jest aktywny?

// ✅ Pozytywne nazwy - jasne
bool czyAktywny = true;
if (czyAktywny) { }       // Jasne!

// ✅ Dobre nazwy bool
bool czyZalogowany;
bool maUprawnienia;  
bool czyWalny;
bool moznaEdytowac;

4. Kontekst ma znaczenie

// W klasie User
public class User
{
    public string Name { get; set; }  // Nie trzeba UserName - kontekst jest jasny
    public int Age { get; set; }      // Nie UserAge
}

// Ale w ogólnej metodzie
public void ProcessUserData(string userName, int userAge)  // Tutaj kontekst jest potrzebny
{
    // kod
}

Ćwiczenia praktyczne

Ćwiczenie 1: Popraw nazwy

Popraw nazwy w poniższym kodzie zgodnie z konwencjami C#:

// ❌ Kod do poprawienia
public class user
{
    public string n;
    private int a;
    public bool flag;
    
    public void GetData(string s, int i)
    {
        // kod
    }
    
    private void process_info()
    {
        // kod
    }
}

public interface calculator
{
    int add(int x, int y);
}

public enum status_types
{
    active,
    inactive,
    pending
}