CodeGym /Kurslar /C# SELF /Generics-kolleksiyaların üstünlükləri

Generics-kolleksiyaların üstünlükləri

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

1. Qısa tarixçə

Köhnə vaxtlarda C#-da kolleksiyalarla işləmək üçün cəmi iki sevinc var idi: massivlərdən istifadə etmək və ya "çox da tipli olmayan" kolleksiyalardan — məsələn, ArrayList, harada istənilən tipdə obyektləri yığmaq olardı. Elə bil azadlıq! Amma belə kolleksiyaya həm sətir, həm də tam ədəd əlavə edəndə — hər şey qarışırdı: elementi təhlükəsiz çıxarmaq olmurdu, əlavə (və həmişə işləməyən) tip yoxlamaları lazım olurdu. Üstəlik — yersiz səhvlər və tip çevirmə (casting) ilə rəqs.

Generic-olmayan kolleksiya nümunəsi (ArrayList):


using System.Collections;

ArrayList stuff = new ArrayList();
stuff.Add(42);
stuff.Add("Salam C#");

int number = (int)stuff[0]; // OK
string text = (string)stuff[1]; // OK

int fail = (int)stuff[1]; // BUM! InvalidCastException (runtime error)

Bəli, kompilyator heç nə demir — səhv yalnız proqram işləyərkən çıxır! Elə bil soyuducunu açırsan və orada isti qazan tapırsan — sürpriz!

Ümumiləşdirmənin mahiyyəti nədir?

Generic kolleksiyalar (Generics) ona görə yaranıb ki, kolleksiyalarda saxlanılan tipə zəmanət verilsin. Bu isə üç əsas üstünlük verir:

  • Kompilyasiya mərhələsində tip təhlükəsizliyi. Kompilyator sənə təsadüfən kolleksiyaya artıq bir şey qoymağa imkan verməyəcək.
  • Rahatlıq: hər dəfə tip çevirməyə ehtiyac yoxdur.
  • Performans: artıq "boxing və unboxing" üçün vaxt itirilmir (boxing/unboxing).

2. Generics (ümumiləşdirmə) nədir ümumiyyətlə?

Generics — bu, məlumat strukturlarını və metodları elə təsvir etməyin sehrli yoludur ki, onlar istənilən tiplə işləyə bilsin, amma yenə də ciddi tipli qalsın.

Təsəvvür elə universal bir qutu var, onu həm kitablar, həm də corablar üçün istifadə edə bilərsən, amma yalnız bir tip üçün eyni anda. Əgər qutu "yalnız kitablar üçün" elan olunubsa — heç kim ora corab ata bilməz. Generic-kolleksiyalarda da eyni: əgər sən int-dən kolleksiya yaradırsansa, ora təsadüfən string düşə bilməz.

Generic sinifin elan olunması nümunəsi


public class Box<T>
{
    public T Value { get; set; }
}

var boxOfInt = new Box<int> { Value = 42 };
var boxOfString = new Box<string> { Value = "Salam Generics!" };

T — bu, "tip parametri"dir, hansı ki, qutunun nə ilə işləyəcəyini göstərir. C# tələb edəcək ki, həmişə dəqiq göstərəsən, hansı tip qoyursan.

.NET-də generic kolleksiyalar

.NET Framework-də demək olar ki, bütün müasir kolleksiyaların generic versiyası var. Bunlar:

  • List<T> — T tipli elementlərin dinamik siyahısı.
  • Dictionary<TKey, TValue> — açar və dəyərlərlə assosiativ massiv (sözlük).
  • Queue<T>, Stack<T> — növbələr və steklər.
  • və bir çox başqaları.

3. Necə işləyir: Generics-in quruluşu

Tip parametrləri

Sən List<int> kimi kolleksiya elan edəndə, C# kompilyatoru bu sinifin xüsusi bir variantını məhz int üçün yaradır. List<string> elan edəndə isə, kompilyator başqa bir variant yaradır, amma artıq sətirlər üçün və s.

Sənin üçün, proqramçı kimi, bu tam sehr kimi işləyir:


List<int> numbers = new List<int>();
numbers.Add(1); // Yalnız int əlavə etmək olar

List<string> words = new List<string>();
words.Add("salam"); // Yalnız string əlavə etmək olar

Başqa tip element əlavə etməyə cəhd etsən, kompilyator dərhal narazılıq edəcək:


numbers.Add("fail"); // KOMPİLYASİYA mərhələsində SƏHV!

Tip təhlükəsizliyi: Compile-time vs. Run-time

Bu o deməkdir ki, yuxarıdakı kimi səhvlər proqramın işləmə mərhələsinə (run-time) çatmayacaq. Sənin kolleksiyan o qədər qorunur ki, ora heç kim yaxın dura bilməz — kompilyator qapıda keşikçi kimi dayanıb.

Qapağın altında

  • Referans tiplər üçün (məsələn, string, object) artıq əlavə tip çevirmələri yoxdur.
  • Qiymətli tiplər üçün (int, double) "boxing/unboxing" problemi yox olur (boxing/unboxing).
  • .NET-də generics istifadə etmək artıq kod bolluğuna gətirmir — CLR bunu JIT-kompilyasiya mərhələsində optimallaşdırır.

4. Generics performansı necə artırır

Gəlin bir daha vurğulayaq ki, Generics kodumuza hansı sehrli xüsusiyyətləri verir:

Tip təhlükəsizliyi (Type Safety):

Bəlkə də ən vacibi budur. Generics sayəsində kompilyator sənin şəxsi mühafizəçin olur, kolleksiyaya "yad" bir şey buraxmır. Sən tam əmin ola bilərsən ki, List<Product> yalnız Product obyektləri saxlayacaq, təsadüfi sətir və ya ədəd yox. Bu, əvvəllər yalnız proqram işləyərkən çıxan və gözlənilməz "partlayışlara" (InvalidCastException) səbəb olan səhvləri tam aradan qaldırır. Kodun daha etibarlı və proqnozlaşdırıla bilən olur.

Performans (Performance):

Dediyimiz kimi, qiymətli tiplər üçün (int, double və ya DateTime) Generics yaddaş və prosessor vaxtı ilə daha səmərəli işləməyə imkan verir. Milyonlarla element saxlayan və ya tez-tez dəyişən kolleksiyalar üçün bu, kritik ola bilər. Portağalları hər dəfə sandıqdan torbaya və əksinə qoymaq əvəzinə, birbaşa lazım olan sandığa qoyursan.

Kodun təkrar istifadəsi (Code Reusability):

Generics sənə bir kod yazmağa imkan verir ki, o, müxtəlif tiplərlə işləsin və kodu təkrarlamağa ehtiyac qalmasın. Məsələn, iki dəyişənin yerini dəyişmək üçün funksiya lazımdır. Generics olmadan sən SwapInt(ref int a, ref int b), SwapString(ref string a, ref string b), SwapProduct(ref Product a, ref Product b) və s. yazmalı idin. Generics ilə isə cəmi bir funksiya yazırsan: Swap<T>(ref T a, ref T b). Bu prinsip kolleksiyalara da aiddir: sənə IntList, StringList, ProductList lazım deyil – List<T> kifayətdir. Kodun daha yığcam, asan dəstəklənən və genişlənən olur.

5. Generic metodlar və öz generic-siniflərin

Generic metodlar

Generics — təkcə kolleksiyalar üçün deyil! Sən öz generic metodlarını yaza bilərsən, hansı ki, istənilən tiplə işləyir. Bu, kodu daha universal edir.


//Tip-parametri T-ni metod adından dərhal sonra göstəririk
public static void Swap<T>(ref T x, ref T y)
{
    T temp = x;
    x = y;
    y = temp;
}

// İstifadə:	
int a = 10, b = 20;
Swap(ref a, ref b); // İndi a == 20, b == 10

string one = "bir", two = "iki";
Swap(ref one, ref two); // Sətirlər üçün də işləyir!

Diqqət et, C# kompilyatoru metodun tip-parametrini ötürülən dəyişənlərin tipinə görə özü müəyyən edə bilir. Yəni yuxarıdakı nümunədə Swap metoduna əlavə tip ötürməyə ehtiyac yoxdur.

Öz generic-siniflərin

Hətta öz "qutularını" (generic-siniflərini) da yarada bilərsən. Bu, düşündüyündən asandır:


public class Pair<TFirst, TSecond>
{
    public TFirst First { get; set; }
    public TSecond Second { get; set; }
}

// İstifadə:
var pair = new Pair<int, string> { First = 42, Second = "cavab" };

6. Generic-kolleksiyalardan istifadə nümunəsi

Gəlin "tapşırıqlar siyahısı"na (To-Do List) qayıdaq, hansı ki, əvvəlki mühazirələrdə nümunə kimi başlamışdıq. Əvvəl tapşırıqları massivdə saxlayırdıq və ya sadəcə ekrana çıxarırdıq. İndi isə — onları dinamik olaraq List<string>-də saxlayırıq.

Nümunə: tapşırıqları siyahıya dinamik əlavə etmək


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> tasks = new List<string>();

        Console.WriteLine("Tapşırıq daxil edin (və ya bitirmək üçün boş sətr):");
        string input;
        while (!string.IsNullOrWhiteSpace(input = Console.ReadLine()))
        {
            tasks.Add(input);
            Console.WriteLine("Tapşırıq əlavə olundu! Daha birini daxil edin (və ya bitirmək üçün boş sətr):");
        }

        Console.WriteLine("\nBu gün üçün tapşırıqlar siyahınız:");
        foreach (string task in tasks)
        {
            Console.WriteLine("- " + task);
        }
    }
}

Burada nə yaxşıdır?

  • Kolleksiya yeni tapşırıqlar əlavə olunduqca avtomatik genişlənir.
  • Siyahıya təsadüfən sətirdən başqa bir şey əlavə etmək olmur.
  • Siyahını asanlıqla gəzib, məzmununu çıxarmaq olur.

7. Başlanğıc üçün tipik səhvlər, xüsusiyyətlər və məsləhətlər

Generic-olmayan kolleksiyalardan generic-lərə keçid bəzən çaşqınlıq yaradır. Budur, yeni başlayanların tez-tez rast gəldiyi bir neçə vəziyyət:

  • Düzgün tip olmayan element əlavə etməyə cəhd (List<int> numbers = new List<int>(); numbers.Add("salam"); // Səhv).
  • Bir kolleksiyada müxtəlif tipləri qarışdırmaq istəyi — burada əsas tipi seçmək lazımdır (məsələn, List<object>) — və sonra həmişə geri lazımi tipə çevirmək lazım gələcək.
  • constraints mövcudluğunu unudurlar, çox "geniş" ümumiləşdirmə yazırlar və kompilyasiya mərhələsində qəribə səhvlərlə qarşılaşırlar.
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION