CodeGym /Kurslar /C# SELF /record-un class və struct-dan fərqləri

record-un class və struct-dan fərqləri

C# SELF
Səviyyə, Dərs
Mövcuddur

1. Ümumiyyətlə niyə record-a ehtiyac yarandı?

C#-da (və ümumiyyətlə .NET-də) uzun müddət əsas tikinti blokları class-lar (class) və struct-lar (struct) idi. Amma hər biri bəzi məsələlər üçün tam deyil. Class-lar — referans tipdir, dəyişkəndir və müqayisə referansa görədir (çox nadir istisna ilə). Struct-lar — dəyər tipidir (ötürüləndə kopyalanır), default olaraq byte-byte müqayisə olunur və çox vaxt dəyişkəndir (ta ki, readonly struct gələnə qədər).

Əgər sənə sadə və təbii data yazısı lazım idisə, hansı ki, dəyərə görə rahat müqayisə olunur, tez klonlanır və heç kim obyektini haradasa dəyişdi deyə baş ağrısı olmur — gərək bir ton "velosiped" düzəldəsən və ya ValueTuple və ya hətta System.Tuple kimi kitabxanalara əl atasan. Amma bunların heç biri istədiyin qədər gözəl və ifadəli deyil.

Ona görə də C# 9-da record gəldi — data tipi, hansı ki, qısa elan, dəyişməzlik təhlükəsizliyi və dəyərə görə müqayisə davranışını birləşdirir.

2. Dörd tip bir səhifədə

class struct record record struct
Kateqoriya Referans tipi Dəyər tipi Referans tipi Dəyər tipi
Dəyişkənlik Default — bəli Default — bəli Default — yox (init) Default — yox (init)
Müqayisə Referansa görə (==) Dəyərə görə (sahələr) Dəyərə görə (fields/properties) Dəyərə görə (fields)
Klonlama Ancaq əl ilə Ancaq əl ilə Daxili dəstək var (with) Daxili dəstək var (with)
Irsiyyət Bəli Yox Bəli Yox
Dəyişməzlik Özün reallaşdırmalısan Özün reallaşdırmalısan Çox asan reallaşdırmaq Çox asan reallaşdırmaq
Sintaksis Ən uzun Qısa Ən qısa (pozisional) Yetərincə qısa
Kolleksiyalarda istifadə Referansa görə Kopyalar Referansa görə Kopyalar

Vizual sxem

+----------------+     +----------------+     +--------------------+
|    class       |     |    struct      |     |      record        |
+----------------+     +----------------+     +--------------------+
| Reference Type |     | Value Type     |     | Reference Type     |
| Mutable        |     | Mutable        |     | Immutable (init)   |
| == : Reference |     | == : By Fields |     | == : By Value      |
+----------------+     +----------------+     +--------------------+

3. record, class və struct arasındakı əsas fərqlər

Yaddaşda davranış: referans yoxsa dəyər?

  • class və record — referans tipdir. Funksiyaya ötürəndə obyektin referansı kopyalanır.
  • struct və record struct — dəyər tipidir. Onlar həmişə byte-byte kopyalanır (yalnız referansla ötürməsən).

class PointClass { public int X; public int Y; }
struct PointStruct { public int X; public int Y; }
record PointRecord(int X, int Y);
record struct PointRecordStruct(int X, int Y);

void Demo()
{
    var pc = new PointClass { X = 1, Y = 2 };
    var ps = new PointStruct { X = 1, Y = 2 };
    var pr = new PointRecord(1, 2);
    var prs = new PointRecordStruct(1, 2);

    ChangeY(pc);    // pc.Y dəyişəcək!
    ChangeY(ps);    // ps.Y dəyişməyəcək — kopya!
    ChangeY(pr);    // pr.Y dəyişəcək!
    ChangeY(prs);   // prs.Y dəyişməyəcək — kopya!
}

void ChangeY(dynamic p) { p.Y = 99; }

Əgər sənə elə gəlir ki, dynamic burada magiyadır — bəli, bu sadəcə nümunə üçündür ki, görəsən, struct-larla işləyəndə dəyər dəyişmir, class və ya record (referans) ilə işləyəndə isə dəyişir.

Müqayisə: iki obyektin bərabər olduğunu necə bilmək olar?

  • class: referansa görə müqayisə olunur (== — true, yalnız eyni obyekt olduqda), əgər Equals override olunmayıbsa.
  • struct: bütün sahələrin dəyərinə görə müqayisə olunur (default olaraq).
  • record: bütün sahə/property-lərin dəyərinə görə müqayisə olunur, hansı ki, əsas konstruktorunda verilib.
class Foo { public int A; public int B; }
record Bar(int A, int B);

var foo1 = new Foo { A = 42, B = 1 };
var foo2 = new Foo { A = 42, B = 1 };
var bar1 = new Bar(42, 1);
var bar2 = new Bar(42, 1);

Console.WriteLine(foo1 == foo2); // False! Fərqli obyektlər
Console.WriteLine(bar1 == bar2); // True! Dəyərlər eynidir

Maraqlı fakt:
record struct ilə daha da maraqlıdır: onlarda müqayisə "dəyərə görədir", adi struct kimi, amma record-un sintaksisi və imkanları ilə.

4. Dəyişməzlik: kim nə qədər zəmanət verir?

Obyektlərin təhlükəsizliyini müqayisə edirik:

  • class: Default olaraq asanlıqla dəyişir, yalnız bütün sahələri readonly etsən dəyişməz olur.
  • struct: Eyni, amma readonly struct kimi elan edə bilərsən, onda nə sahələri, nə property-ləri dəyişmək olar.
  • record: Adətən sahələr init modifier-i ilə elan olunur, yəni onları yalnız konstruktor və ya obyekt initializer-də (with) təyin etmək olar. Bu, data ötürmək üçün təhlükəsizdir.
  • record struct: Eyni, readonly record struct edə bilərsən və dəyər tipində dəyişməzlik və record-un bonuslarını əldə edirsən.

record Person(string Name, int Age);

var p1 = new Person("Aleksey", 23);
// p1.Age = 24; // Səhv! Yalnız init

var p2 = p1 with { Age = 24 }; // İşləyir! Yeni data ilə kopya yaradır

Əgər böyük layihən var və onlarla entity-obyektin varsa, record bir ton bug-dan qaçmağa kömək edəcək, hansı ki, "kim harada sahəni dəyişdi — hər şey dağıldı".

5. Sintaksis: elanlar necə görünür və necə çaşmamaq olar


// class
public class Product
{
    public int Id { get; init; }
    public string Name { get; init; }
}

// struct
public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

// record
public record Product(int Id, string Name);

// record struct
public record struct Point(int X, int Y);
Müxtəlif tiplərin elan sintaksisinin müqayisəsi

Gördüyün kimi, record tipində yazı maksimum qısadır. Konstruktor, destruktor, bərabərlik, ToString() və bir çox şeyi "qutudan çıxan" kimi alırsan.

Sintaksis Pulsuz əldə etdiklərin Irsiyyət mümkündürmü
class demək olar ki, heç nə Bəli
struct demək olar ki, heç nə Yox
record ToString, Equals, Deconstruct, with Bəli
record struct ToString, Equals, Deconstruct, with Yox

6. Klonlama və with operatoru

Yalnız record və record struct-da xüsusi with operatoru var, hansı ki, obyektin bəzi property-lərini dəyişərək rahat kopyalamağa imkan verir.


record User(string Name, int Age);

var user1 = new User("İrina", 28);
var user2 = user1 with { Age = 29 }; // user2.Name == "İrina", user2.Age == 29

Class-da (class) isə kopyalama metodunu özün yazmalısan, hansı ki, bir sahəni kopyalamağı unutsan — salam, bug-lar.

7. Irsiyyət: kim kimi

  • class: Standart irsiyyəti dəstəkləyir (class iyerarxiyası, virtual metodlar, abstraksiyalar və s.).
  • struct: Dəstəkləmir (yalnız interface-ləri implement edə bilər).
  • record: Irsiyyəti dəstəkləyir, amma bəzi məhdudiyyətlərlə (məsələn, yalnız record-lar arasında irsiyyət mümkündür, class ilə yox).
  • record struct: Dəstəkləmir, adi struct kimi.
record Animal(string Name);
record Dog(string Name, string Breed) : Animal(Name); // Ok!

class Vehicle { }
class Car : Vehicle { } // Ok!

// struct struct-dan irs ala bilmir

Irsiyyət barədə daha ətraflı növbəti səviyyədə :P

8. Hansı tiplər praktikada harada istifadə olunur

  • class: Böyük obyektlər, zəngin davranış, uzun ömür, dəyişkən vəziyyət, iyerarxiya (məsələn, biznes loqikası, UI komponentləri və s.).
  • struct: Kiçik dəyər obyektləri, harada ki, maksimal sürətli kopyalama, GC olmaması və minimal overhead vacibdir (məsələn, koordinatlar, rənglər, məbləğlər — asan və tez klonlanan şeylər).
  • record: DTO (Data Transfer Object), parametr obyektləri, konfiqurasiya parametrləri, dəyişməz vəziyyətlər, nəticələr — hansılar ki, məzmuna görə müqayisə etmək rahatdır.
  • record struct: Dəyər tipidir, dəyişməzlik və record davranışı lazımdır, amma heap-də əlavə allocation olmadan.

9. Tipik səhvlər və gizli tələlər

Bəzən proqramçılar düşünür ki, record sadəcə "class-ın əvəzi"dir. Elə deyil! Əgər obyektin həyatında vəziyyəti dəyişməlidirsə — class istifadə et.
Əgər obyektləri referansa görə müqayisə etmək lazımdırsa (məsələn, singleton pattern-də və ya obyektin ömrü kritikdirsə) — class istifadə et.
Əgər dəyər kimi davranan obyekt lazımdırsa, məsələn, rəqəm və ya koordinat nöqtəsi kimi — struct və ya record struct.
Əgər dəyişməz, rahat müqayisə olunan obyektlər, hansılar ki, tez-tez tətbiq qatları arasında ötürülür, kolleksiyalarda saxlanılır, loglanır və serializasiya olunur — sənin seçimin record-dur.

Bir də yadında saxla: əgər record-class elan etmisənsə və property-lər yalnız oxunandırsa, amma nested obyektləri unutmusansa — nested sahələr yenə də dəyişə bilər, əgər özləri dəyişkəndirsə.


record Student(string Name, List<int> Grades);

var s1 = new Student("Anton", new List<int>() {5,5,5});
var s2 = s1 with { };

s2.Grades.Add(2); // Hər iki obyekt "lənətlənib" iki ilə! s1.Grades == s2.Grades
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Record yaradılması və obyektlərin müqayisəsi
Record yaradılması və obyektlərin müqayisəsi
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
`with` operatorundan istifadə edərək məlumatların yazılması
`with` operatorundan istifadə edərək məlumatların yazılması
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
DTO ilə bağlı problem
DTO, record və with
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION