🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Class, struct czy record w C#? Kiedy używać którego typu

“Jaka jest różnica między class a struct?” to jedno z najczęstszych pytań rekrutacyjnych na Junior .NET Developera — i jedno z tych, na które większość kandydatów odpowiada teoretycznie (“struct to typ wartościowy”), nie potrafiąc pokazać, co to realnie oznacza w działającym kodzie. Dołóż do tego record (C# 9+) i record struct (C# 10+) i robi się cztery narzędzia do wyboru zamiast dwóch.

Class — referencja, tożsamość, mutowalność

public class Zamowienie
{
    public int Id { get; set; }
    public decimal Kwota { get; set; }
}

var a = new Zamowienie { Id = 1, Kwota = 100 };
var b = new Zamowienie { Id = 1, Kwota = 100 };

Console.WriteLine(a == b); // False — to dwa różne obiekty w pamięci

class to typ referencyjny — zmienna przechowuje adres obiektu na stercie, nie same dane. Domyślne porównanie == sprawdza, czy to ten sam obiekt w pamięci, nie czy mają te same wartości. To sensowne dla bytów z tożsamością (np. Zamowienie o konkretnym Id, które zmienia stan w czasie) — bez sensu dla prostych danych, gdzie liczy się wartość, nie tożsamość.

Struct — wartość, kopiowanie, brak alokacji na stercie

public struct Punkt
{
    public int X, Y;
}

var p1 = new Punkt { X = 1, Y = 2 };
var p2 = p1;        // KOPIA całych danych, nie referencja
p2.X = 99;

Console.WriteLine(p1.X); // 1 — zmiana w p2 nie dotyka p1

struct to typ wartościowy — przypisanie kopiuje całą zawartość. To samo dzieje się przy przekazaniu do metody:

void ZmienPunkt(Punkt p) => p.X = 999; // zmienia tylko lokalną kopię

var punkt = new Punkt { X = 1, Y = 2 };
ZmienPunkt(punkt);
Console.WriteLine(punkt.X); // wciąż 1 — metoda dostała kopię

Zaleta: małe struktury nie trafiają na stertę zarządzaną przez GC (żyją na stosie), więc są tanie w tworzeniu i niszczeniu. Pułapka: duże struktury są drogie w kopiowaniu — każde przypisanie i przekazanie do metody kopiuje wszystkie pola. Microsoft rekomenduje trzymać się rozmiaru do ok. 16 bajtów.

Record — równość wartościowa “za darmo”

public record Pieniadze(decimal Kwota, string Waluta);

var x = new Pieniadze(100, "PLN");
var y = new Pieniadze(100, "PLN");

Console.WriteLine(x == y); // True — te same wartości = równe obiekty
Console.WriteLine(x);      // Pieniadze { Kwota = 100, Waluta = PLN } — ToString gotowy od ręki

var z = x with { Kwota = 150 }; // nowa, niezależna kopia z jedną zmienioną wartością
Console.WriteLine(x.Kwota);     // 100 — oryginał bez zmian

record (domyślnie record class) jest wciąż typem referencyjnym — dane trafiają na stertę tak jak w zwykłej klasie. Różnica: kompilator generuje za ciebie porównanie wartościowe (Equals/GetHashCode po polach), czytelny ToString() oraz operator with do tworzenia zmodyfikowanej kopii bez ręcznego przepisywania wszystkich pól. To dokładnie to, co ręcznie dopisuje się do klas reprezentujących dane (DTO, value objects) — tu masz to bez linijki dodatkowego kodu.

Record struct — połączenie obu światów

public readonly record struct Wspolrzedne(double Szerokosc, double Dlugosc);

var w1 = new Wspolrzedne(52.23, 21.01);
var w2 = new Wspolrzedne(52.23, 21.01);

Console.WriteLine(w1 == w2); // True — równość wartościowa, i bez alokacji na stercie

record struct (C# 10+) to struct z dodatkami record: kopiowanie przez wartość i brak alokacji na stercie jak w zwykłym struct, plus darmowa równość wartościowa i ToString() jak w recordreadonly na początku wymusza niemutowalność — połączenie, które ma sens dla małych, niezmiennych danych, gdzie zależy ci na wydajności (np. współrzędne, kolor RGB, para wartości zwracana z metody).

Tabela decyzyjna

TypPamięćKopiowanieRówność domyślnaKiedy użyć
classstertaprzez referencjętożsamość (referencja)byty z tożsamością i zmiennym stanem (encje domenowe)
structstos (zwykle)przez wartośćpola, ale bez ładnego ToStringmałe, wydajnościowo krytyczne wartości
recordstertaprzez referencjęwartość (pola)niemutowalne DTO, value objects, dane API
record structstos (zwykle)przez wartośćwartość (pola)małe, niemutowalne wartości + potrzebujesz równości/ToString

Najczęstsza pułapka: mutowalny struct

public struct LicznikZly
{
    public int Wartosc;
    public void Zwieksz() => Wartosc++;
}

Struktury, które można modyfikować po utworzeniu, to źródło trudnych do znalezienia bugów — bo w zależności od tego, czy operujesz na oryginale czy na jednej z wielu niewidocznych kopii (np. z foreach, z właściwości tylko-do-odczytu), zmiana albo zadziała, albo zniknie bez śladu. Zasada: struktury powinny być niemutowalne (readonly struct), a jeśli potrzebujesz “zmienić” wartość — zwróć nową, zamiast modyfikować istniejącą.

Podsumowanie

  • class — gdy liczy się tożsamość obiektu i jego stan zmienia się w czasie.
  • record — gdy modelujesz dane, dla których “te same wartości = ten sam obiekt logicznie” (DTO, value objects), i chcesz darmową równość, ToString() i with.
  • struct — gdy potrzebujesz małej, wydajnej wartości i chcesz uniknąć alokacji na stercie.
  • record struct — gdy chcesz obu tych rzeczy naraz: wydajności struct i wygody record.

Domyślny wybór dla nowych typów w nowoczesnym C#: jeśli dane mają być niemutowalne, sięgnij po record (albo record struct, jeśli są małe i wydajność ma znaczenie). Po zwykłą class sięgaj świadomie — gdy naprawdę potrzebujesz tożsamości i mutowalnego stanu.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →