Menu

Zmienne w C#: var, const, readonly, zakres i nazewnictwo

Jak deklarować zmienne i przypisywać im wartości w C#, kiedy var to dobry pomysł, a kiedy nie, jak działa zakres, czym różnią się const, readonly i static readonly, konwencje nazewnictwa i błąd nieprzypisanej zmiennej.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Zmienna to nazwane miejsce w pamięci o stałym typie. W C# deklarujesz ją typem i nazwą, a opcjonalnie w tej samej instrukcji nadajesz jej wartość. Typ potem się nie zmienia: zmienna int przechowuje liczby całkowite przez cały czas swojego istnienia.

Wynik:

Priya bought 5 items for 20.25
10

Przypisanie wartości złego typu to błąd kompilacji: quantity = "five"; kończy się błędem CS0029 (cannot implicitly convert type 'string' to 'int'). Konwersja między typami jest jawna; zobacz konwersja typów.

var: wnioskowanie typu przez kompilator

var deklaruje zmienną lokalną, której typ jest wnioskowany z inicjalizatora. To nie jest typowanie dynamiczne: typ jest ustalony w czasie kompilacji, dokładnie tak, jakby był zapisany wprost.

Wynik:

String
Int32
Decimal
Double
List`1

var ma trzy zasady. Działa tylko dla zmiennych lokalnych (nie dla pól ani parametrów). Zmienna musi być zainicjalizowana w deklaracji: var x; to błąd CS0818, "Implicitly-typed variables must be initialized". A inicjalizator musi mieć typ: var nothing = null; się nie kompiluje, bo samo null nie ma typu.

Kiedy go używać, to kwestia stylu zespołu. Typowa wskazówka: używaj var, gdy typ jest oczywisty z prawej strony, a zapisuj typ, gdy nie jest:

var orders = new List<Order>();         // obvious: var is fine
var reader = new StreamReader(path);    // obvious
decimal discount = CalculateDiscount(); // not obvious: name the type
int count = items.Count();              // makes the intent clear

Subtelna pułapka: var total = 0; czyni total zmienną typu int, więc późniejsze dodanie do niej 2.5 się nie uda, a var price = 10; to int, a nie decimal. Gdy typ literału nie jest tym, którego chcesz, zapisz typ albo dodaj przyrostek (10m, 0.0).

Zakres

Zmienna lokalna istnieje od deklaracji do końca bloku { }, w którym ją zadeklarowano. Zmienne zadeklarowane w nagłówku pętli albo w ciele if nie są widoczne za nimi.

Wynik:

high
240

W przeciwieństwie do C i C++ C# nie pozwala, żeby blok wewnętrzny zadeklarował zmienną lokalną o tej samej nazwie co zmienna w bloku otaczającym (błąd CS0136): nazwa musi znaczyć jedno w całej metodzie. Dwa sąsiednie bloki mogą użyć tej samej nazwy, tak jak dwie osobne pętle for, z których każda deklaruje i.

Jednoznaczne przypisanie

Zmienne lokalne nie mają wartości domyślnej. Kompilator śledzi każdą ścieżkę w metodzie i odmawia kompilacji, jeśli zmienna może zostać odczytana, zanim zostanie jej przypisana wartość:

int discount;
if (isMember)
{
    discount = 10;
}
Console.WriteLine(discount);
// error CS0165: Use of unassigned local variable 'discount'

Napraw to, inicjalizując zmienną przy deklaracji (int discount = 0;) albo przypisując wartość w każdej gałęzi (dodaj else). Pola i elementy tablic działają inaczej: automatycznie dostają wartość domyślną swojego typu (0, false, null).

const: stałe czasu kompilacji

const to wartość ustalona w czasie kompilacji. Musi być zainicjalizowana wyrażeniem stałym (literałem, inną stałą albo działaniami na nich) i może być tylko liczbą, char, bool, string, enumem albo null.

Wynik:

Price in EUR
120.00 EUR
350

Pole const jest niejawnie static, więc odwołujesz się do niego przez nazwę typu (Pricing.VatRate), a zapis static const to błąd. Kompilator kopiuje wartość do każdego miejsca, które jej używa, dlatego stałe nic nie kosztują w czasie działania i dlatego publiczna stała w bibliotece powinna być wartością, która nigdy się nie zmieni (jak liczba dni w tygodniu).

readonly i static readonly

Pole readonly można przypisać tylko w jego deklaracji albo w konstruktorze jego klasy. Po utworzeniu obiektu nie może się zmienić. W przeciwieństwie do const jego wartość jest obliczana w czasie działania, może być dowolnego typu i może się różnić między obiektami.

Wynik:

ACC-17 opened 2024-05-02
Launch: 2020-01-15
basic, pro

Ostatnie linie pokazują częste nieporozumienie: readonly chroni pole, a nie obiekt, na który wskazuje. Plans zawsze będzie odnosić się do tej samej tablicy, ale elementy tablicy nadal mogą się zmieniać. Do naprawdę niemutowalnych kolekcji używaj ReadOnlyCollection<T> albo ImmutableArray<T>.

Podsumowanie trzech wariantów:

constreadonlystatic readonly
Wartość znanaW czasie kompilacjiW czasie działaniaW czasie działania
Dozwolone typyLiczby, char, bool, string, enumy, nullDowolneDowolne
Ustawiane wTylko w deklaracjiW deklaracji lub konstruktorzeW deklaracji lub konstruktorze statycznym
Na obiekt czy współdzieloneWspółdzielone (niejawnie static)Na obiektWspółdzielone
Zmienne lokalneTak (const int x = 1;)NieNie

Konwencje nazewnictwa

Nazwy w C# rozróżniają wielkość liter (total i Total to różne zmienne) i muszą zaczynać się od litery lub podkreślnika. Konwencje Microsoftu, których przestrzega niemal cały kod C#:

  • Zmienne lokalne i parametry: camelCase (orderCount, unitPrice).
  • Stałe, właściwości, metody, klasy i pola publiczne: PascalCase (MaxItems, VatRate).
  • Pola prywatne: _camelCase z podkreślnikiem na początku (_balance), rozpowszechniona konwencja w kodzie .NET.
  • Zmienne logiczne czytają się jak pytania: isActive, hasDiscount, canRetry.
  • Unikaj skrótów i przedrostków węgierskich (strName, iCount).

Słowo kluczowe C# nie może być nazwą, chyba że poprzedzisz je znakiem @: var @class = "5B"; jest dozwolone, co czasem przydaje się w kodzie generowanym albo przy nazwach pól JSON.

Zamiana i wielokrotne przypisanie

Od C# 7 krotki pozwalają przypisać kilka zmiennych w jednej instrukcji, co daje zamianę w jednej linii:

Wynik:

20 10
Lisbon: 545,000

Druga forma to dekonstrukcja: z krotki deklaruje city jako string i population jako int. Więcej w krotkach.

Najczęściej zadawane pytania

Czym jest var w C#?

var każe kompilatorowi wywnioskować typ zmiennej lokalnej z wartości, którą jest inicjalizowana: var count = 5; deklaruje int. Zmienna nadal ma typ statyczny i nie może później przechowywać stringa. var działa tylko dla zmiennych lokalnych inicjalizowanych w tej samej instrukcji.

Jaka jest różnica między const a readonly w C#?

const to stała czasu kompilacji: jej wartość musi być znana podczas kompilacji (liczby, stringi, bool, null), jest niejawnie statyczna, a wartość jest kopiowana do każdego miejsca, które jej używa. Pole readonly jest ustawiane raz w czasie działania, w deklaracji albo w konstruktorze, może być dowolnego typu i może się różnić między obiektami.

Kiedy używać static readonly zamiast const?

Używaj static readonly dla współdzielonej wartości, która nie może być stałą czasu kompilacji, na przykład DateTime, tablicy albo obiektu, albo dla publicznej wartości, która może się zmienić między wersjami biblioteki. Ponieważ wartości const są kopiowane do wywołujących zestawów, zmiana publicznej stałej wymaga ponownej kompilacji każdego zestawu, który jej używa.

Co oznacza "use of unassigned local variable" w C#?

Błąd CS0165: kompilator znalazł ścieżkę, na której zmienna lokalna jest odczytywana, zanim przypisano jej jakąkolwiek wartość. Zmienne lokalne w C# nie mają wartości domyślnej. Zainicjalizuj zmienną przy deklaracji albo upewnij się, że każda gałąź if/switch przypisuje jej wartość przed użyciem.

Używać var czy jawnych typów w C#?

Oba podejścia są w porządku; zespoły wybierają swój styl. Częsta zasada: używaj var, gdy typ jest oczywisty z prawej strony (var list = new List<string>();), a zapisuj typ, gdy nie jest (decimal total = GetTotal();). Kod źródłowy środowiska .NET mniej więcej trzyma się tej zasady.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ