1. Kolleksiyalar haqqında bir neçə söz
İndiyə qədər biz massivlərlə işləyirdik — bu, eyni tipli bir neçə elementi saxlamağın ən sadə yoludur. Amma C#-da müxtəlif tapşırıqları həll edən bir çox daha rahat və güclü kolleksiya var.
Kolleksiya — başqa obyektlər qrupunu saxlaya bilən obyekt deməkdir. Massivlərdən fərqli olaraq, kolleksiyalar çox vaxt ölçüsünü dinamik dəyişə bilir, məlumatlarla işləmək üçün rahat metodlar təqdim edir və müxtəlif istifadə ssenariləri üçün optimallaşdırılıb.
Əsas kolleksiya tipləri (qısa icmal):
List<T> — böyüyə və kiçilə bilən dinamik massiv:
List<string> names = new List<string>();
names.Add("Aleks");
names.Add("Mariya");
Console.WriteLine(names[0]); // Aleks
Console.WriteLine(names.Count); // 2
Dictionary<TKey, TValue> — "açar-dəyər" cütlərindən ibarət kolleksiya, burada hər açara bir dəyər uyğun gəlir:
Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Aleks"] = 25;
ages["Mariya"] = 30;
Console.WriteLine(ages["Aleks"]); // 25
Diqqət et: yuxarıdakı nümunələrdə kolleksiyaların elementlərinə giriş üçün kvadrat mötərizələrdən [] istifadə edirik — tam massivlərdəki kimi! Bu isə indeksatorlar sayəsində mümkündür, bu gün də məhz onlardan danışacağıq.
Bu, kolleksiyalarla ilk tanışlıqdır — onların imkanlarını, fərqlərini və tətbiqini növbəti mühazirələrdə ətraflı öyrənəcəyik. İndi əsas odur ki, kolleksiyalar elementlərinə kvadrat mötərizələrlə müraciət etməyə imkan verir və bu, heç bir sehr deyil, dilin xüsusi mexanizmidir.
2. İndeksatorlar
Adi C# obyektləri xüsusiyyətlər (properties) və metodlar vasitəsilə işləyir. Amma bəs sənin obyektin kiçik bir kolleksiya olarsa? Məsələn, təsəvvür elə:
- Week adlı bir sinif yazırsan, o həftənin gününün adını nömrə ilə qaytarmalıdır: week[0] → "Bazar ertəsi".
- Və ya Library sinifi, burada kitaba nömrə ilə müraciət etmək olar: library[3] → "Svan tərəfə".
Rahat səslənir, düzdür? Hər dəfə library.GetBookByIndex(3) yazmaq qəribə olardı — istərdin ki, obyektinə massiv kimi müraciət edəsən!
Bax, burada indeksatorlar köməyə gəlir.
İndeksator — bu, sinifin xüsusi üzvüdür, obyektlərə massivlərdəki kimi kvadrat mötərizələrlə müraciət etməyə imkan verir: obj[0], obj["açar"] və s.Belə indeksator xaricdən xüsusiyyət kimi görünür, amma ad əvəzinə parametrləri kvadrat mötərizədə qəbul edir. Bu, .Name yazmağa bənzəyir, sadəcə burada [i] yazırsan.
3. İndeksatorlu sadə kolleksiya
Gəlin, sevimli rəngləri saxlayan mini-sinif yığaq. Saxlamaq üçün string massivindən istifadə edirik. İndeksator olmasa, GetColor(int i) kimi metod yazmalı idik. Amma indeksatorla:
using System;
public class FavoriteColors
{
// Rəngləri saxlamaq üçün private sahə
private string[] colors = new string[5];
// İndeksator:
public string this[int index]
{
get
{
// Massivin sərhədlərinə nəzarət (inkapsulyasiya iş başında!)
if (index < 0 || index >= colors.Length)
throw new IndexOutOfRangeException("Yanlış rəng indeksi!");
return colors[index];
}
set
{
if (index < 0 || index >= colors.Length)
throw new IndexOutOfRangeException("Yanlış rəng indeksi!");
colors[index] = value ?? throw new ArgumentNullException(nameof(value));
}
}
}
class Program
{
static void Main()
{
FavoriteColors favorites = new FavoriteColors();
favorites[0] = "Yaşıl";
favorites[1] = "Göy";
favorites[2] = "Qırmızı";
favorites[10] = "Bənövşəyi"; // İstisna atır!
Console.WriteLine(favorites[1]); // Göy
}
}
Burada nə baş verir?
- Private massiv yaradırıq ki, kənardan heç kim birbaşa qarışa bilməsin.
- İndeksator public string this[int index] kimi təyin olunub, burada this — indeksatorun obyektə aid olduğunu göstərir.
- get və set içində sərhədləri yoxlayırıq, kənara çıxmağa və ya null yazmağa icazə vermirik.
- Nəticədə favorites[0] kimi, adi massivdəki kimi istifadə edə bilirik.
4. İndeksatorun sintaksisi: detallı
Sintaksis xüsusiyyətə bənzəyir, amma ad (məsələn, Age) əvəzinə this açar sözü və parametrlər yazılır:
// İndeksatorun imzası (ümumi şablon)
[modifikator] NəticəTipi this[İndeksTipi indeksAdi]
{
get { ... }
set { ... }
}
Nümunə: Klassik
public class MyCollection
{
private int[] data = new int[10];
// Oxuma və yazma üçün indeksator
public int this[int index]
{
get { return data[index]; }
set { data[index] = value; }
}
}
İndeksatorlar təkcə int ilə olmur
Əsas fənd: indeksator yalnız int qəbul etməlidir deyə bir qayda yoxdur. İstənilən tip verə bilərsən (əsas odur ki, açar üzrə müraciət mənalı olsun):
public string this[string colorName]
{
get { /* ... */ }
set { /* ... */ }
}
Məsələn, telefon kitabçası sinifində ada görə axtarmaq məntiqlidir:
public class PhoneBook
{
private Dictionary<string, int> entries = new Dictionary<string, int>();
public int this[string name]
{
get
{
if (entries.ContainsKey(name))
return entries[name];
return null;
}
set
{
entries[name] = value;
}
}
}
Kolleksiyalar və Dictionary<string, string> necə işləyir — gələcək mühazirələrdə danışacam :P
5. Praktik nümunə: Mətnin söz sayğacı
Proqramımızı inkişaf etdiririk. Tutaq ki, indi bir sinifimiz var, o, mətnin içində hər bir sözün neçə dəfə rast gəldiyini sayır. İstəyirik ki, istifadəçi obyektə kvadrat mötərizələrlə müraciət edib, sözə görə sayını ala bilsin:
using System.Collections.Generic;
public class WordCounter
{
private Dictionary<string, int> counter = new Dictionary<string, int>();
// Sözə görə indeksator
public int this[string word]
{
get
{
if (counter.ContainsKey(word))
return counter[word];
return 0; // Belə söz yoxdursa, 0 qaytarırıq.
}
set
{
counter[word] = value;
}
}
// Sətirdən sözləri saymaq üçün metod
public void AddWords(string text)
{
foreach (var word in text.Split(' ', System.StringSplitOptions.RemoveEmptyEntries))
{
if (counter.ContainsKey(word))
counter[word]++;
else
counter[word] = 1;
}
}
}
// Main-da:
var wc = new WordCounter();
wc.AddWords("mama myla ramu myla mama papa");
Console.WriteLine($"'mama' rast gəlinir {wc["mama"]} dəfə");
Console.WriteLine($"'ramu' rast gəlinir {wc["ramu"]} dəfə");
Console.WriteLine($"'kot' rast gəlinir {wc["kot"]} dəfə"); // 0
Bu praktikada nə üçün lazımdır? Belə yanaşma tez-tez öz kolleksiyalarını, yaddaş kitabxanalarını, mapping-ləri (sözlüklər və indekslər) və hətta DSL (C# daxilində xüsusi dillər) yaratmaq üçün istifadə olunur.
6. Məhdudiyyətlər və incəliklər
İndeksatorlar güclü şeydir, amma bir neçə qayda və tələlər var (onsuz da olmur axı).
İndeksatorun adı olmur
Xüsusiyyətdən fərqli olaraq, indeksatorun adı yoxdur, yalnız this[parametr tipi] şəklində imzası var. Əgər public int MyIndexer[int i] yazmağa başlasan — kompilyator təəccüblənəcək. Yalnız this istifadə edirik.
Statik indeksator olmur
İndeksatorlar həmişə sinifin nümunəsi üçün yaradılır, statik üzvlər üçün yox. Yəni static int this[int i] elan etmək olmaz — məntiq ondadır ki, this həmişə konkret obyekt nümunəsinə işarə edir.
Parametrin tipi/sayı üzrə overload etmək olar
Bir sinifdə bir neçə indeksator yarada bilərsən, əgər onların parametrləri tip və ya say baxımından fərqlidirsə. Məsələn:
public string this[int i] { get { ... } set { ... } }
public string this[string key] { get { ... } set { ... } }
Bu, normaldır, kompilyator çaşmaz — və parametrlər üst-üstə düşsə, xəbər verəcək.
Yalnız accessor-lar vasitəsilə dəstəklənir
get və ya set olmadan indeksator elan etmək olmaz. Yalnız oxumaq üçün istəyirsənsə — set sil, yalnız yazmaq üçün — get sil. Adətən hər ikisi olur.
7. Praktik fayda və bunu niyə bilmək lazımdır
- İndeksatorlar məlumat kolleksiyalarında aktiv istifadə olunur. Bir çox .NET sinifində var: məsələn, List<T>, Dictionary<TKey,TValue>. list[2] yazanda, məhz indeksatordan istifadə edirsən!
- İndeksatorlar daxili implementasiyanı gizlətməyə (inkapsulyasiya!) imkan verir, amma rahat və tanış interfeys təqdim edir. Sənin sinifinin istifadəçisi məlumatı necə saxladığını düşünmür, sadəcə tanış [index] istifadə edir.
- Kodun qısa və intuitiv olur — bu, müsahibələrdə (və gələcək kolleqaların tərəfindən) xüsusilə qiymətləndirilir.
Xüsusiyyətlər və İndeksatorlar: müqayisə
| Xüsusiyyət | İndeksator | |
|---|---|---|
| Ad | Bəli (məsələn, Name) | Xeyr (ad əvəzinə — this[parametr]) |
| Giriş | Adla | İndeks (və ya başqa açar) ilə |
| Statika | static ola bilər | Yalnız nümunə üçün |
| Sinifdə çox | Bəli, istənilən sayda | Bəli, amma parametr imzası fərqli olmalıdır |
| Tətbiq | Məlumat saxlama/giriş | Mini-kolleksiyalar, assosiativ məlumatlar |
8. Tipik səhvlər və məsləhətlər
Səhv №1: indeksatoru static etmək cəhdi.
Olmaz — this[...] yalnız obyektlə işləyir.
Səhv №2: indeks yoxlamasını unutmaq.
Əgər get və ya set içində sərhədləri yoxlamasan, proqram qəza ilə bağlana bilər.
Səhv №3: parametr tipləri qarışıb.
Eyni parametrlərlə iki indeksator yaratsan, kompilyator səhv verəcək.
Səhv №4: get və ya set implementasiyasını unutmaq.
Əgər həm oxumaq, həm yazmaq lazımdırsa — hər ikisi olmalıdır.
Məsləhət: əgər sinifin massivə wrapper-dirsə — müraciətləri indeksatorla birbaşa massivin özünə ötür. Bu, işi sürətləndirəcək və kodu intuitiv edəcək.
9. Bunu niyə bilmək lazımdır
İndeksatorlar obyektin interfeysini sadə, başadüşülən və rahat edir. Onlar daxili implementasiyanı gizlətməyə imkan verir, amma proqramçıya məlumatlara rahat giriş üsulu saxlayır.
Məhz belə daxili kolleksiyalar qurulub: string, List<T>, Dictionary<TKey, TValue>, Span<T> və bir çox başqaları. array[2] və ya text[0] yazanda, artıq indeksatordan istifadə edirsən.
Ən əsası — artıq öz siniflərini yaza bilərsən ki, onlar da belə çevik və qısa işləyir. Deməli, sən artıq peşəkar və oxunaqlı kod yazmağa bir addım daha yaxınsan.
GO TO FULL VERSION