1. Giriş
Təmiz funksiya — eyni giriş verilənlərində həmişə eyni nəticəni qaytaran və heç bir yan təsir törətməyən funksiyadır. Sadə desək: təmiz funksiya ətrafı "korlamır" və xaricdən təsirlənmir.
Funksiya təmiz hesab olunur, əgər:
- O, yalnız giriş arqumentlərinə əsasən qiymət hesablayır.
- Xarici dəyişənləri və ya proqramın vəziyyətini dəyişdirmir.
- Dəyişə bilən xarici dəyişənlərə və ya vəziyyətlərə asılı deyil.
Təmizliyin iki qızıl qaydası
- Deterministiklik: eyni giriş — eyni çıxış.
- Yan təsirlərin olmaması: funksiya özündən kənarda heç nəyi dəyişmir: faylları, qlobal dəyişənləri, istifadəçi interfeysini (salam, Console.WriteLine).
Din deyil, ağıl
Bu akademik bir ovuc quyruq kimi görünə bilər, amma praktika göstərir:
- Təmiz funksiya proqnozlaşdırılandır. Onu test etmək asandır — müəyyən arqumentlə çağırsınız və müəyyən nəticə əldə edirsiniz.
- Təmiz funksiyanı kodda sərbəst "daşına" bilərsiniz, heç nəyin harasa qırılacağından qorxmaq lazım deyil.
- Çoxnüvəli dünyada təmiz funksiya təhlükəsizdir: onu paralel işlətmək olar, data race-dən qorxmadan.
2. Təmiz və natəmiz funksiyalara nümunələr
Təmiz funksiya: hər şey proqnozlaşdırılandır
// Təmiz funksiya: özündən kənarı toxunmur
int Add(int a, int b)
{
return a + b;
}
int Square(int x)
{
return x * x;
}
Add(2, 3) yüz dəfə çağırılsa da — həmişə 5 olacaq. Sıkıcı, amma etibarlıdır.
Natəmiz funksiya: qaydaları pozur
// Təmizliyi pozur: xarici vəziyyətdən asılıdır (statik dəyişən)
int counter = 0;
int Increase()
{
counter++;
return counter;
}
Burada Increase() hər çağırışda yeni dəyər qaytaracaq — yəni artıq deterministik deyil.
// Təmizliyi pozur: xarici effekt yaradır (ekrana çap)
int AddAndPrint(int a, int b)
{
int sum = a + b;
Console.WriteLine(sum); // Yan təsir!
return sum;
}
Təsadüfi və zamanla bağlı nə olar?
DateTime.Now və ya Random istifadə edən hər funksiya artıq təmiz deyil:
// Təmiz deyil!
int GetRandomNumber()
{
return new Random().Next();
}
Fərqlərin cədvəli
| Xüsusiyyət | Təmiz funksiya | Natəmiz funksiya |
|---|---|---|
| Eyni arqumentlər üçün həmişə eyni nəticə | Bəli | Xeyr |
| Yan təsirlər | Xeyr | Bəli |
| Xarici vəziyyətdən asılılıq | Xeyr | Bəli |
3. Məlumatların dəyişməzliyi: nəzəriyyə və praktik
Dəyişməzlik (immutability) — obyekt yaradıldıqdan sonra onu dəyişmək mümkün olmadığı yanaşmadır. Yeni dəyərə ehtiyac varsa — yeni obyekt yaradırıq.
Niyə bu vacibdir?
- Tətbiq təsadüfi məlumat dəyişikliyinə daha davamlı olur.
- Gizli "dəyişiklik sızmaları" olmur: əgər sizdə bir obyekt varsa, heç kim onun sahəsini gizlicə dəyişməyəcək.
- Dəyişməzlik bir çox avtomatik optimizasiyaların və paralel hesablamaların əsasıdır.
C#-də sadə nümunələr
.NET-də dəyişməz tiplər
C#-da sətirlər (string) dəyişməzdir! Hər dəfə string.Concat(s, "world") etdikdə yeni sətir yaranır.
string s = "Hello";
string t = s;
s = s + " World";
Console.WriteLine(t); // t == "Hello"
Massivlər və kolleksiyalar: default üzrə mutable
int[] numbers = { 1, 2, 3 };
numbers[0] = 42; // Massiv dəyişdi!
Dəyişməzliyi "barmaqla izah": kodun kompilyasiyası
Mövcud obyekt/dəyəri dəyişdirmək əvəzinə yeni qaytarırıq:
// Bunun əvəzinə:
void AddToList(List<int> list, int value)
{
list.Add(value); // Mutasiya edir!
}
// Daha yaxşı:
List<int> AddToList(List<int> list, int value)
{
var newList = new List<int>(list) { value }; // Yeni siyahı
return newList;
}
İllüstrasiya: dəyişdir — dəyişdirmə
flowchart LR
A[İlkin obyekt] --"mutasiya"--> B[eyni obyekt, amma içi fərqli]
A --"dəyişməzlik"--> C[yeni obyekt]
4. Real C# kodunda niyə lazım?
- Müasir C# kitabxanaları kimi LINQ, Entity Framework və ASP.NET Core təmiz funksiyalar və dəyişməzliyə önəm verirlər.
- Dəyişməzlik "sehrli" xətaların sayını azaldır, kiminsə haradasa vacib dəyəri sildiyi hallar azalır.
- Təmiz funksiyalar unit-testləri asanlaşdırır, çünki testi yazmaq üçün yalnız giriş və çıxışı yoxlamaq kifayətdir, xarici dünya ilə məşğul olmaq lazım deyil.
Nümunə: sətirlərlə işləmək
string s = "Hello";
string newS = s.Replace("H", "J"); // s hələ də "Hello"; newS — "Jello"
Nümunə: LINQ və kolleksiyalar
Where, Select və digər metodlar yeni kolleksiyalar qaytarır, köhnələri toxunmur.
var numbers = new List<int> { 1, 2, 3, 4 };
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
// numbers olduğu kimi qaldı!
Real həyatdan nümunə: konfiqurasiya dəyişməz obyektlərlə
Bir çox müasir .NET API-ləri konfiqurasiya üçün dəyişməz obyektlərdən istifadə edir, məsələn JsonSerializerOptions:
var options = new JsonSerializerOptions
{
WriteIndented = true
};
// Bu obyekt "oyun zamanı" dəyişmir, bu etibarlılığı artırır.
5. Tipik səhvlər və tələlər
Sürüşkən yer o zaman başlayır ki, siz "təmiz" kodda təsadüfən məlumatı mutasiya edirsiniz.
Bu çox vaxt kolleksiyalarla olur: siyahını filterləmək istədiniz, amma prosesdə ilkin siyahını dəyişdiniz.
Yaxud unutdunuz ki, List<T>.Add metodu obyekti yerindəcə dəyişdirir.
Pis nümunə:
List<int> DoubleTheNumbers(List<int> xs)
{
// Səhv! İlkin siyahını mutasiya edirik, eyni obyekti qaytarırıq.
foreach (var i in xs)
xs.Add(i * 2);
return xs;
}
Bu kod icra zamanı xətaya (InvalidOperationException) səbəb olacaq, çünki biz kolleksiyanı iterasiya edərkən dəyişdiririk — mutasiyanın klassik problemi.
Düzgün:
List<int> DoubleTheNumbers(List<int> xs)
{
var newList = new List<int>(xs.Select(x => x * 2));
return newList;
}
GO TO FULL VERSION