1. Giriş
C#-da property-siz proqram yazmaq olar, amma bu, sanki Ağ Evdə roliklərlə gəzmək kimidir — mümkündür, amma heç rahat deyil. İndi artıq əlavə getter və setter-lər olmadan qısa sintaksisə öyrəşmişik, amma app-imiz “ciddi” modellərlə işləməyə başlayanda (məsələn, sistemdə istifadəçini təsvir edən User class-ı), vacib olur ki, bütün lazım olan datalar həqiqətən də mövcud olsun.
Məsələn, əgər belə bir class-ımız varsa:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
Property-ləri initialize etməyi asanlıqla unuda bilərik:
User user = new User(); // Name null olacaq, Age = 0
Nəticədə, hardasa on ekran və yüz logic döngəsindən sonra məşhur NullReferenceException alacaqsan və günahkarı tapmaq üçün çox vaxt itirəcəksən.
init-only property-ləri xatırlayaq
Bəli, yalnız yaradılarkən initialize etmək olar:
public string Name { get; init; }
Amma bu sintaksislə belə, heç kim class istifadəçisini dəyər ötürməyə məcbur etmir — əksər default constructor-lar field-ləri default dəyərlə initialize edir (null, 0 və s.).
Bəs necə edək ki, proqramçı və ya özümüz vacib dəyəri unutmayaq? Məhz bunun üçün C# 11-də required modifier-i icad olunub.
2. required property-lər: sərt initialize qaydası
required modifier-i kompilyatora deyir ki, obyekt yaradılarkən bu property mütləq şəkildə təyin olunmalıdır. Sadə dillə desək, əgər belə property initialize olunmayıbsa — kompilyator kodu buraxmayacaq, IDE isə qorxulu qırmızı xətt çəkəcək.
public class User
{
public required string Name { get; set; }
public int Age { get; set; }
}
Gəlin istifadəçi yaradaq:
// Kompilyasiya xətası: Name property-si initialize olunmalıdır!
User user1 = new User();
Və ya belə:
// Kompilyasiya xətası: required property Name göstərilməyib
User user2 = new User { Age = 18 };
Doğru yol isə belədir:
User user3 = new User { Name = "Hermiona", Age = 18 };
required necə işləyir?
required modifier-i kompilyatora deyir: “Obyektin constructor-u bitəndən sonra, bu property mütləq təyin olunmalıdır”.
Bu həm adi property-lər, həm də init-only üçün işləyir:
public required string Name { get; init; }
Əgər öz constructor-un varsa və required property üçün dəyər təyin edirsənsə, qayda pozulmur.
Tipik yoxlama sxemi
| Ssenari | Kompilyator razıdır? |
|---|---|
| required property təyin olunmayıb | ❌ |
| Obyekt initializer-də təyin olunub | ✔️ |
| Constructor-da təyin olunub | ✔️ |
Bizim “İt” modelində nümunə
public class Dog
{
public required string Name { get; set; }
public int Age { get; set; }
}
Dog dog = new Dog { Name = "Bobik", Age = 5 }; // Hər şey ok!
Dog badDog = new Dog { Age = 2 }; // Xəta! Name göstərilməyib
required harada həqiqətən kömək edir?
- Layer-lər arasında DTO ötürüləndə: Əgər API-n varsa, input-da həmişə bütün vacib field-lər gəlməlidir.
- Vacib atributlu kompleks modellər: Məsələn, Product-da mütləq SKU, Order-də mütləq nömrə.
- Müsahibə və kod review zamanı: Belə sintaksis göstərsən, yəqin ki, kodunu hörmətlə (və bir az paxıllıqla) oxuyacaqlar.
3. required və constructor-lar necə işləyir?
Bəzən öz constructor-un yazırsan. Əgər required property constructor-da və ya obyekt initializer-də initialize olunmasa nə baş verəcək? Kompilyator xəta verəcək.
public class Article
{
public required string Title { get; set; }
public required string Author { get; set; }
public Article()
{
// Əgər Title və Author initialize olunmasa — kompilyasiya xətası!
// Belə etmək olar:
Title = "Başlıqsız";
Author = "Naməlum";
}
}
Əgər constructor required property-lərə dəyər verir — hər şey qaydasındadır. Əks halda, bu field-ləri obyekt initializer-lə (new Article { ... }) initialize etməlisən.
İstifadə spesifikası
- required yalnız property-lərlə işləyir, field-lərlə yox.
- required miras alınmır — əgər base property required-dursa, amma child-da required yazmasan, kompilyator narazı olmur (amma yaxşı practice odur ki, child-da da required yazasan).
- required avtomatik field-lərə və ya property-dən başqa heç nəyə tətbiq edilə bilməz.
4. Property-lərin arrow sintaksisi
C#-ın yeni versiyaları ilə dev-lər daha qısa və ifadəli kod yazmağa can atır. Property-lər üçün ən gözəçarpan yeniliklərdən biri — arrow sintaksisi (yəni expression-bodied properties).
Bəzən elə property yazmaq istəyirsən ki, sadəcə bir dəyəri qaytarsın, əlavə logic olmadan. Əvvəllər bunun üçün tam getter yazmaq lazım idi:
public int Age
{
get { return birthYear > 0 ? DateTime.Now.Year - birthYear : 0; }
}
İndi isə çox daha qısa yazmaq olar — arrow (=>) istifadə edərək:
public int Age => birthYear > 0 ? DateTime.Now.Year - birthYear : 0;
Bu sintaksisə expression-bodied property deyilir. Sadə hesablama üçün əladır və kodu daha kompakt edir.
get və set ilə istifadə nümunəsi
public class Book
{
private string _title;
public string Title
{
get => _title;
set => _title = value.Trim();
}
}
Burada get field-ın dəyərini qaytarır, set isə əvvəlcə artıq boşluqları silib təyin edir.
Yalnız get (read-only) istifadə nümunəsi:
Əgər property read-only-dursa — heç bir curly bracket olmadan, sadəcə => ilə yaza bilərsən.
public class Person
{
private string name = "Mark Tven";
// Yalnız oxumaq üçün: hesablama property-si
public string Name => name.ToUpper();
}
Burada Name property-si yalnız oxunur — həmişə name-i böyük hərflərlə qaytarır.
5. Property-lər üçün field açar sözü
C# 14-dən əvvəl, əgər avtomatik property-nin gizli field-ına setter və ya getter-də müraciət etmək istəyirdin (məsələn, recursion-dan qorunmaq və ya əlavə logic yazmaq üçün) — bunu etmək olmurdu. Field-ı ayrıca elan etməli idin.
C# 14 avtomatik property-nin gizli field-ına field açar sözü ilə müraciət etməyə imkan verir:
public class Person
{
public string Name
{
get => field; // field — Name property-nin gizli field-ıdır
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Ad boş ola bilməz!");
field = value; // _name əvəzinə field istifadə edirik
}
}
}
Əvvəl belə yazmalı idin:
private string _name;
public string Name
{
get => _name;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Ad boş ola bilməz!");
_name = value;
}
}
Bir dəyişən elan etməyə ehtiyac qalmadı. Kod nə qədər qısa olsa, bir o qədər yaxşıdır.
Bəzən property daxilində məhz field-a müraciət etmək niyə vacibdir?
- Bəzən dəyərin harada və necə saxlanmasını tam nəzarətdə saxlamaq lazımdır (məsələn, obyektin kopyasını vermək, nəticəni cache etmək və ya lazy-loading etmək istəyirsənsə).
- Əgər attribute və ya reflection istifadə edirsənsə — field adı lazım ola bilər.
- Bəzi (de)serialization və ya performance tuning hallarında dəyərin saxlanmasına daha çox nəzarət etmək istəyirsən.
İstifadə nümunələri:
Setter-də data validation
public double Grade
{
get => field;
set
{
if (value < 0 || value > 5)
throw new ArgumentOutOfRangeException("Qiymət 0-dan 5-ə qədər olmalıdır");
field = value;
}
}
Dəyərin dəyişməsinə görə statistika
public int StepCount
{
get => field;
set
{
if (value > field)
{
Console.WriteLine($"Ura! Sən {value - field} addım çox atmısan!");
}
field = value;
}
}
Lazy Load
public string Data
{
get
{
if (field == null)
field = LoadDataFromDatabase();
return field;
}
set => field = value;
}
6. Tipik səhvlər və nüanslar
Səhv №1: required property-ni initialize etməyi unutmaq.
Kompilyator belə kodu buraxmayacaq və build zamanı dərhal xəta verəcək, bu da proqram işləyərkən problemlərin qarşısını alır.
Səhv №2: required property-lər constructor-da tam initialize olunmayanda işləmir.
Əgər constructor bütün vacib property-lər üçün dəyər almırsa, kompilyator sənə xəbər verəcək ki, nəsə yaddan çıxıb.
Səhv №3: required-i const və ya readonly ilə istifadə etmək cəhdi.
Bu modifier-lər uyğun deyil — required yalnız adi property-lərə tətbiq oluna bilər. Onları birləşdirməyə cəhd etsən, xəta alacaqsan.
GO TO FULL VERSION