static oznacza składową, która należy do typu, a nie do obiektu. Pole instancji istnieje raz na obiekt; pole statyczne istnieje raz i kropka. Metoda instancyjna działa na obiekcie (order.Ship()); metoda statyczna działa na typie (Math.Round(x)) i w ogóle nie potrzebuje obiektu.
Pola statyczne: jedna kopia, współdzielona
Najprościej zobaczyć różnicę na liczniku. Każde Order ma własne Id, ale liczba dotąd utworzonych zamówień musi być przechowywana w jakimś wspólnym miejscu.
Wynik:
Ana #1, Ben #2, Chen #3
Orders created: 3
Do składowych statycznych odwołuje się przez nazwę typu: Order.Created, a nie a.Created. C# nie pozwala sięgać do składowej statycznej przez instancję (błąd CS0176), dzięki czemu w miejscu wywołania od razu widać, że wartość jest współdzielona.
Metody statyczne
Metoda statyczna nie ma this. Pracuje tylko ze swoimi parametrami i innymi składowymi statycznymi. Dlatego to naturalny kształt dla funkcji pomocniczych, które obliczają coś z danych wejściowych:
Wynik:
61.38 EUR
7
Samo Main jest statyczne: środowisko uruchomieniowe wywołuje je, zanim powstanie jakikolwiek obiekt.
Błąd CS0120: kod statyczny używa składowych instancji
Ponieważ Main jest statyczne, początkujący często trafiają na ten błąd, gdy tylko dodadzą drugą metodę:
class Program
{
int visits;
void Greet() { Console.WriteLine("Hello"); }
static void Main()
{
Greet(); // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
visits++; // error CS0120: An object reference is required for the non-static field, method, or property 'Program.visits'
}
}
Metoda statyczna nie ma obiektu, więc nie ma dla niej żadnego visits do zwiększenia. Są dwa rozwiązania, a właściwe zależy od tego, czym jest składowa:
- Składowa to naprawdę dane konkretnego obiektu: utwórz obiekt i użyj go.
var app = new Program(); app.Greet(); - Składowa nie potrzebuje stanu obiektu: oznacz ją jako
static.static void Greet() { ... }
Oznaczanie wszystkiego jako static, żeby błąd zniknął, działa w malutkim programie, a w prawdziwym zamienia się w stan globalny. Zdecyduj, pytając, czy dane należą do jednego obiektu, czy do całego programu.
W drugą stronę zawsze wolno: metody instancyjne mogą swobodnie wywoływać składowe statyczne.
Klasy statyczne
Klasa zadeklarowana jako static może zawierać tylko składowe statyczne i nigdy nie można utworzyć jej instancji. To nazwana grupa funkcji i stałych.
Wynik:
77
100
True
Kompilator pilnuje tej umowy: składowa instancji w klasie statycznej to błąd CS0708, a new na niej to błąd CS0712. Math, Console, File, Path i Enumerable to klasy statyczne. Metody rozszerzające również muszą znajdować się w klasie statycznej.
Konstruktory statyczne i static readonly
Pole statyczne, którego wartość wymaga więcej niż jednego wyrażenia, można ustawić w konstruktorze statycznym, który środowisko uruchomieniowe wykonuje raz, przed pierwszym użyciem typu:
Wynik:
Before first use
ShippingRates initialized
5.50
3
2026-01-01
readonly oznacza, że pole można przypisać tylko w jego inicjalizatorze albo w konstruktorze statycznym. Potem nie da się go ponownie przypisać, choć obiekt, na który wskazuje, nadal może być modyfikowalny, i dlatego słownik jest udostępniony jako IReadOnlyDictionary.
const a static readonly
Oba dają nazwaną wartość współdzieloną przez cały program, a różnią się tym, kiedy wartość zostaje ustalona:
const | static readonly | |
|---|---|---|
| Wartość ustalana | w czasie kompilacji | w czasie działania, raz |
| Dozwolone typy | liczby, char, bool, string, enumy, null | dowolny typ |
| Niejawnie statyczne | tak (nie można napisać static const) | statyczne deklarowane jawnie |
| Kopiowane do wywołujących assembly | tak | nie, odczytywane z pola |
Użycie w przypadkach switch i argumentach atrybutów | tak | nie |
Wiersz o kopiowaniu ma znaczenie dla bibliotek: jeśli assembly A deklaruje public const int MaxItems = 50;, a assembly B go używa, B zawiera literał 50. Zmiana w A na 100 nic nie daje w B, dopóki B nie zostanie ponownie skompilowane. Dla wartości, które mogą się zmieniać między wersjami, wybieraj static readonly.
using static
Dyrektywa using static (C# 6) importuje statyczne składowe typu, więc możesz je wywoływać bez nazwy typu:
Wynik:
Area: 28.27
40
Dobrze się to czyta w kodzie pełnym obliczeń matematycznych. Gdzie indziej używaj tego oszczędnie: samo WriteLine albo Parse ukrywa, z którego typu pochodzi metoda.
Stan statyczny i wątki
Pole statyczne jest współdzielone przez każdy wątek w procesie. Zwiększanie go jednocześnie z kilku wątków gubi aktualizacje, bo created++ to odczyt, dodawanie i zapis, a dwa wątki mogą je przeplatać. Do liczników używaj Interlocked.Increment(ref created), a do czegokolwiek większego lock. Najczęściej daje to o sobie znać w aplikacjach webowych: każde żądanie działa na jakimś wątku, więc pole statyczne przechowujące "bieżącego użytkownika" jest wspólne dla wszystkich użytkowników.
Jeszcze jeden niuans: w klasie generycznej każdy skonstruowany typ dostaje własne pola statyczne. Cache<int>.Count i Cache<string>.Count to dwa różne pola.
Kiedy używać static
Dobre zastosowania:
- Czyste funkcje pomocnicze, które obliczają wynik ze swoich argumentów: formatowanie, konwersje, walidacja.
- Stałe i dane wyszukiwania tylko do odczytu współdzielone przez program.
- Metody fabrykujące, takie jak
TimeSpan.FromMinutes(5)albo twoje własneTemperature.FromCelsius(21). - Metody rozszerzające.
Złe zastosowania:
- Modyfikowalny stan globalny (bieżący użytkownik, koszyk, zmieniające się ustawienia). Wiąże ze sobą każdy fragment kodu, który go dotyka, i uzależnia testy od pozostałości po innych testach.
- Wszystko, co zechcesz podmienić w testach, na przykład zegar, bazę danych albo nadawcę e-maili. Wywołań statycznych nie da się zastąpić atrapą; instancję przekazaną do konstruktora da się.
Najczęściej zadawane pytania
Co oznacza static w C#?
Składowa static należy do samego typu, a nie do żadnego obiektu. Pole statyczne istnieje w jednej kopii bez względu na to, ile jest obiektów, a metodę statyczną wywołuje się na nazwie typu (Math.Max(3, 7)) bez tworzenia instancji. Metoda statyczna nie ma this, więc nie może bezpośrednio używać pól instancji.
Czym jest klasa statyczna w C#?
Klasa zadeklarowana jako static może zawierać tylko składowe statyczne i nigdy nie można utworzyć jej instancji (new na niej to błąd CS0712). Grupuje powiązane funkcje, które nie potrzebują stanu obiektu, jak Math, Console czy File. Metody rozszerzające muszą być zadeklarowane w klasie statycznej.
Jak naprawić błąd "An object reference is required for the non-static field, method, or property"?
To błąd CS0120: metoda statyczna, często Main, używa składowej instancji. Albo utwórz obiekt i wywołaj składową na nim (var app = new Program(); app.Run();), albo oznacz składową jako static, jeśli nie potrzebuje danych konkretnego obiektu.
Jaka jest różnica między const a static readonly w C#?
const to stała czasu kompilacji (liczby, stringi, bool, enumy), której wartość jest kopiowana do każdego assembly, które jej używa. Pole static readonly jest przypisywane raz w czasie działania, w inicjalizatorze albo w konstruktorze statycznym, i może przechowywać dowolny typ, na przykład DateTime albo listę. Używaj const dla wartości, które nigdy się nie zmienią, a static readonly dla wszystkiego innego.
Kiedy używać static w C#?
Używaj static dla kodu, który działa tylko na swoich parametrach (czyste funkcje pomocnicze, na przykład formatowanie ceny), dla stałych i cache naprawdę globalnych dla programu oraz dla metod fabrykujących. Unikaj static dla wszystkiego, co przechowuje stan konkretnego użytkownika lub żądania albo co zechcesz podmienić w testach: współdzielony modyfikowalny stan trudno testować i bez blokad nie jest bezpieczny między wątkami.