CodeGym /Kurslar /C# SELF /record və pozisional s...

record və pozisional sintaksislə tanışlıq

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

1. Məlumat ötürülməsi problemi

Tutaq ki, bizdə məktəb üçün bir tətbiq var və tələbə haqqında məlumatı müxtəlif modullar arasında ötürməliyik: ad, doğum ili, sinif. Adətən necə edirik?

Bir class yaradırıq, məsələn:


public class Student
{
    public string Name { get; set; }
    public int YearOfBirth { get; set; }
    public string Class { get; set; }
}
Tələbə məlumatlarını saxlamaq üçün class (köhnə yanaşma)

Adiləşmiş görünür. Amma bu yanaşmanın bir neçə problemi var:

  • İki tələbəni bərabərlik üçün müqayisə edəndə, default olaraq sadəcə obyektin referansı müqayisə olunur. Yəni sahələri eyni olan iki tələbə, amma fərqli obyektlər — bərabər deyil!
  • Class-ı yaratdıqdan sonra dəyişmək olar, bu isə bəzən səhvlərə gətirib çıxarır (xüsusilə obyekt artıq haradasa istifadə olunursa);
  • Çoxlu “şablon” kod: konstruktorlar, müqayisə metodları, kopyalama (klonlama), ToString.

Və yəqin artıq hiss etmisən: C# bizi bu dərdlərdən xilas edə bilər! Qarşıla — record.

2. record nədir?

record — C#-da xüsusi bir tipdir, əsasən məlumat saxlamaq üçün yaradılıb. Record-un iki əsas feyçeri var:

  1. Dəyişməzlik (immutability): record tipli obyektlər default olaraq dəyişməzdir, yəni onların property-ləri yaradılarkən bir dəfə təyin olunur və sonra dəyişmir (əslində, set-setter-lər private olur). Düzdür, dəyişən record-lar da elan etmək olar, amma default olaraq dəyişməzlikdir.
  2. Dəyərə görə müqayisə: əgər iki record obyektinin bütün sahələrinin dəyərləri eynidirsə, onlar bərabər sayılır (==.Equals() fərqli işləyir!).

Əslində, record — tətbiqin qatları arasında məlumat ötürmək üçün ideal bir qabdır (məsələn, DB-dən controller-ə, controller-dən view-ə və s.).

3. Record-un sintaksisi

Ən sadə yol — pozisional sintaksis

Sadəcə bir neçə dəyəri ötürmək lazım olanda, tipi bir cümlə ilə elan edirik:


public record Student(string Name, int YearOfBirth, string Class);
Record-un pozisional sintaksisi

Nə baş verir? Kompilyator özü bizim üçün aşağıdakıları generasiya edir:

  • Yalnız oxumaq üçün avtomatik property-lər (private setter-lə);
  • Bütün parametrləri qəbul edən konstruktor;
  • Müqayisə və kopyalama metodları;
  • Super ToString, normal formatlanmış çıxış üçün!

Pozisional record-dan istifadə

Gəlin bu yeni tipdən məktəb tətbiqimizdə istifadə edək:

var student1 = new Student("İvan", 2008, "8A");
var student2 = new Student("Mariya", 2008, "8B");

Property-lərə adi qaydada müraciət edirik (sadəcə onları dəyişmək olmur):

Console.WriteLine($"{student1.Name}, {student1.YearOfBirth}, {student1.Class}");

Yaradıldıqdan sonra property-ni dəyişmək cəhdi

student1.Name = "Petr"; // Səhv! Property yalnız oxumaq üçündür.

Əgər bu sətri uncomment etsən — kompilyator dərhal narazılıq edəcək: yalnız oxumaq üçün olan property-yə dəyər vermək mümkün deyil.

Avtomatik generasiya olunmuş ToString belə görünür

Console.WriteLine(student1); // Çıxış: Student { Name = İvan, YearOfBirth = 2008, Class = 8A }

Hər şey gözəl və aydın, heç bir əl formatlaması olmadan!

4. Record-obyektlərin müqayisəsi

Xatırladım: əgər eyni məlumatlarla iki fərqli klassik class obyekti yaratsan, onlar yenə də bir-birinə bərabər olmayacaq:

var a = new Student("İvan", 2008, "8A");
var b = new Student("İvan", 2008, "8A");

Console.WriteLine(a == b); // Class üçün: false

Əgər Student record-dursa, bərabərlik sənin istədiyin kimi işləyir:


public record Student(string Name, int YearOfBirth, string Class);

var a = new Student("İvan", 2008, "8A");
var b = new Student("İvan", 2008, "8A");

Console.WriteLine(a == b); // Record üçün: true!
Record-obyektlərin dəyərə görə müqayisəsi

Yəni sahələri eyni olan iki record, hətta fərqli obyektlər olsa belə, bir-birinə bərabərdir.

5. Bu içəridə necə işləyir

Tələbələr tez-tez təəccüblənir ki, record üçün kompilyator nə qədər iş görür. Gəlin müqayisə edək: class üçün əllə nə qədər kod yazmalı idik və record nə edir.

Köhnə, əllə yazılmış class

public class Student
{
    public string Name { get; }
    public int YearOfBirth { get; }
    public string Class { get; }

    public Student(string name, int yearOfBirth, string @class)
    {
        Name = name;
        YearOfBirth = yearOfBirth;
        Class = @class; //öz class-ına referans
    }

    public override bool Equals(object? obj)
    {
        if (obj is not Student other) return false;
        return Name == other.Name && YearOfBirth == other.YearOfBirth && Class == other.Class;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Name, YearOfBirth, Class);
    }

    public override string ToString()
    {
        return $"Student {{ Name = {Name}, YearOfBirth = {YearOfBirth}, Class = {Class} }}";
    }
}

Təəccüblü deyil ki, proqramçılar paranoyak olur — axı eyni şeyi 10 dəfə yazırıq!

Record — bir sətrə sığır


public record Student(string Name, int YearOfBirth, string Class);
Record — eyni şey, amma bir sətrə!

6. Record və dəyişməzlik: nə olar, nə olmaz

Record-larda default olaraq property-lər yalnız oxumaq üçündür və bu bir çox bug-un qarşısını alır. Amma çox istəsən (məsələn, çox köhnə API üçün yazırsansa), dəyişən record da elan edə bilərsən:

public record MutableStudent
{
    public string Name { get; set; }
    public int YearOfBirth { get; set; }
    public string Class { get; set; }
}

İndi bu sahələri dəyişmək olar, amma bəzı üstünlükləri itirirsən (məsələn, təhlükəsizlik).

7. Record-un destrukturizasiyası

Pozisional sintaksis tuple-lara çox bənzədiyindən, record-u asanlıqla destrukturizasiya etmək olar:


var student = new Student("İvan", 2008, "8A");

var (name, year, className) = student;

Console.WriteLine($"{name} - {year}, {className}"); // İvan - 2008, 8A
Pozisional record-un destrukturizasiyası

Kompilyator hər pozisional record üçün Deconstruct metodu generasiya edir və bu, LINQ, switch-pattern-lərlə işləməyi və ümumiyyətlə həyatı asanlaşdırır.

8. Record — class-dır, yoxsa struct?

Default olaraq recordreferans tipidir, yəni class kimi. Yəni referans tiplərin bütün xüsusiyyətləri (heap-də saxlanma, referansın kopyalanması və s.) adi qaydada işləyir.

Əgər value type istəyirsənsə, C#-da bunun üçün də operator var — belə yaza bilərsən:


public record struct Point(int X, int Y);
Record struct — value type record

Amma məlumat ötürmək üçün demək olar ki, həmişə klassik record forması, yəni referans tipi istifadə olunur. record struct haqqında daha ətraflı — yaxın dərslərdə :P

class, struct, record müqayisəsi

Tip Default dəyişməzlik Dəyərə görə müqayisə Asan destrukturizasiya Auto ToString
class Yox Yox (referansa görə) Yox Yox
struct Yox Bəli Yox Yox
record Bəli Bəli Bəli Bəli

9. Xüsusiyyətlər və tipik səhvlər

Bir çox yeni başlayanlar record-lardan istifadə zamanı nüanslarda çaşırlar. Məsələn, bəzən gözləyirlər ki, bir record obyektində sahəni dəyişəndə, digərində də dəyişsin (class-larda referans kopyalananda olduğu kimi). Yox! Üstəlik, with yazanda unutma ki, həmişə kopya yaradılır, orijinal dəyişmir. Record-lar, tətbiqində məlumatın təmizliyinə və proqnozlaşdırıla bilənliyinə önəm verəndə idealdır.

Bəli, əgər sahələri init ilə elan etmisənsə, set əvəzinə, onları da yalnız yaradanda və ya with ilə təyin edə bilərsən, amma sonra yox.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION