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 kluczowegoWielkość 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 kodu | Konwencja | Przykład |
|---|---|---|
| Zmienne lokalne | camelCase | int wiekUzytkownika; |
| Parametry metod | camelCase | void Metoda(string nazwaParametru) |
| Pola prywatne | _camelCase | private string _imie; |
| Właściwości | PascalCase | public string Imie { get; set; } |
| Metody | PascalCase | public void ObliczSume() |
| Klasy | PascalCase | public class MojaKlasa |
| Interfejsy | IPascalCase | public interface IUzytkownik |
| Stałe | PascalCase | public const int MaxValue = 100; |
| Enumeracje | PascalCase | public enum Status { Aktywny } |
| Namespaces | PascalCase | namespace 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 hTTPResponse2. 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
}