1. Giriş
Əgər IEnumerable<T> ilə biz sadəcə kolleksiyanın mövcud məzmununda gəzişə bilirdiksə, ICollection<T> interfeysi — kolleksiyanı idarə etmək üçün sənin biletindir: əlavə et, sil, elementlərin sayını yoxla, onları massivə kopyala və hətta dəyişiklikləri izləyə bilərsən (demək olar ki, real həyatda alış-veriş səbətinin içindəkiləri nəzarətdə saxlamağa çalışdığın kimi).
ICollection<T> .NET-də bütün dəyişilə bilən kolleksiyalar tərəfindən implementasiya olunur, məsələn List<T>, HashSet<T>, Dictionary<TKey, TValue>.ValueCollection və hətta az tanınan Queue<T>, Stack<T> (bəli-bəli, hətta növbə və steklər də!). Bu, məzmununu dəyişməyə imkan verən bütün kolleksiya sinifləri üçün əsas kontraktdır.
Kolleksiya interfeysləri ailəsi
graph TD
A[IEnumerable<T>]
B[ICollection<T>]
C[IList<T>]
D[IDictionary<TKey, TValue>]
E[Queue<T>, Stack<T>, List<T>, HashSet<T>...]
A --> B
B --> C
B --> D
B --> E
2. ICollection<T> interfeysinə nələr daxildir?
IEnumerable<T>-dən fərqli olaraq, hansı ki, sadəcə iterasiya (foreach) üçündür, ICollection<T> — interfeyslər arasında elə bil İsveçrə bıçağıdır: hər iş üçün bir metod var!
Əsas məzmunu budur:
public interface ICollection<T> : IEnumerable<T>
{
int Count { get; }
bool IsReadOnly { get; }
void Add(T item);
void Clear();
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item);
}
Metod və property-lərin siyahısı:
- Count — kolleksiyadakı elementlərin sayı. Sənin sevimli sayma funksiyasının analoqu.
- IsReadOnly — kolleksiyanı dəyişmək olar? Əgər true isə, kolleksiya yalnız oxumaq üçündür (məsələn, bəzi wrapper-lər üçün).
- Add(T item) — kolleksiyaya element əlavə edir. Sənin əsas alətlərindən biri!
- Clear() — bütün elementləri silir. Tam təmizləmə.
- Contains(T item) — tez yoxlama: bu element kolleksiyada var?
- CopyTo(T[] array, int arrayIndex) — elementləri adi massivə kopyalayır, müəyyən indeksdən başlayaraq. Xarici API-lərə və ya "köhnə" metodlara məlumat ötürmək üçün faydalıdır.
- Remove(T item) — elementi silir (əgər varsa).
3. Proqramçının real tapşırıqları
Niyə bu interfeysin necə işlədiyini başa düşmək vacibdir? Praktikada tez-tez elə kod yazırsan ki, istənilən kolleksiya ilə işləyir. Məsələn, funksiya istənilən siyahı, növbə və ya çoxluq qəbul edə bilər — və fərq etmir, hansı konkret kolleksiya tipidir. Əsas odur ki, bu kolleksiya ICollection<T>-ı implementasiya edir, sən əlavə edə, silə, ölçüsünü yoxlaya və hətta massivə kopyalaya bilərsən. Bu, çevik kitabxanalar və universal alqoritmlər üçün əsasdır.
Həyatdan nümunə: ICollection<T> interfeysi tez-tez metod parametrlərində və API-lərdə rast gəlinir, hansı ki, dəyişmək üçün kolleksiya qaytarır. Məsələn, məşhur ORM-lərdə (Entity Framework, Dapper) ICollection<T> tipli property-lər əlaqəli obyektlərin kolleksiyasını təsvir etmək üçün istifadə olunur.
İstifadə nümunələri
Gəlin universal bir metod yazaq, hansı ki, istənilən kolleksiyanı qəbul edir və ora element əlavə edir. Əsas şərt — o, ICollection<T>-ı implementasiya etməlidir.
// Element əlavə etmək üçün universal metod
void AppendItem<T>(ICollection<T> collection, T item)
{
collection.Add(item);
}
İndi bu metoddan həm List<T>, həm də HashSet<T> ilə, hətta öz interfeys implementasiyanla istifadə edə bilərsən! İstifadə belə görünür:
var numbers = new List<int> { 1, 2, 3 };
AppendItem(numbers, 42);
var uniqueNames = new HashSet<string> { "Alice", "Bob" };
AppendItem(uniqueNames, "Charlie");
Hətta gülməlidir — bizə fərqi yoxdur, siyahıdır, yoxsa çoxluq, əsas odur ki, bu ICollection<T>-dır. Təəccüblənmə, .NET-in içində universal metodlar çox vaxt belə qurulub!
4. Digər kolleksiyalarla əlaqə
ICollection<T> — təkcə nəzəri cəhətdən gözəl kontrakt deyil, həm də .NET-də rast gələcəyin demək olar ki, bütün kolleksiyalar üçün real fundamentdir.
Bunun nə qədər universal olduğunu praktikada görmək üçün aşağıdakı kodu yoxla:
void ShowCollectionInfo<T>(ICollection<T> collection)
{
Console.WriteLine($"Elementlərin sayı: {collection.Count}");
Console.WriteLine($"Dəyişmək olar: {!collection.IsReadOnly}");
Console.WriteLine("Elementlər:");
foreach (var item in collection)
{
Console.WriteLine(item);
}
}
// List<T> üçün
var list = new List<string> { "alma", "portağal" };
ShowCollectionInfo(list);
// HashSet<T> üçün
var set = new HashSet<string> { "alma", "portağal" };
ShowCollectionInfo(set);
Nəticə istənilən kolleksiya tipi üçün eyni düzgün olacaq, əsas odur ki, bu interfeysi implementasiya etsin. Növbə, stek, hətta wrapper massiv üçün də yoxla!
5. Yalnız oxumaq üçün kolleksiyalar
ICollection<T> interfeysində IsReadOnly property-si var, hansı ki, kolleksiyanı dəyişmək olar ya yox, göstərir. Bunu massivlərdəki adi property ilə qarışdırma! Əgər kolleksiya IsReadOnly == true isə, element əlavə və ya silmək cəhdi istisna atacaq.
Nümunə:
// Massiv yaradıb onu ReadOnlyCollection-a bükürük
var array = new int[] { 1, 2, 3 };
ICollection<int> readOnly = Array.AsReadOnly(array);
// Element əlavə etməyə çalışırıq
try
{
readOnly.Add(4); // NotSupportedException atılacaq
}
catch (NotSupportedException)
{
Console.WriteLine("Element əlavə etmək olmaz: kolleksiya yalnız oxumaq üçündür!");
}
Bu, məsələn, xarici mənbədən kolleksiya aldıqda və onu dəyişmək mümkün olmadıqda rast gəlinir — amma elementləri oxumaq və sayını öyrənmək olar.
6. ICollection<T> prizmasından məşhur kolleksiyaların metodlarının müqayisə cədvəli
| Kolleksiya | Add() | Remove() | Contains() | Clear() | CopyTo() | IsReadOnly |
|---|---|---|---|---|---|---|
|
Bəli | Bəli | Bəli | Bəli | Bəli | Xeyr |
|
Bəli* | Bəli | Bəli | Bəli | Bəli | Xeyr |
|
Xeyr/Xeyr | Xeyr/Xeyr | Xeyr/Xeyr | Bəli/Bəli | Bəli/Bəli | Xeyr |
|
Xeyr | Xeyr | Bəli | Xeyr | Bəli | Bəli |
|
Xeyr | Xeyr | Bəli | Xeyr | Bəli | Bəli/Xeyr* |
HashSet<T> üçün Add metodu true qaytarır, əgər element əlavə olunubsa, əks halda false.
7. CopyTo — nə vaxt lazım ola bilər
CopyTo metodu kolleksiyanın məzmununu tez bir zamanda adi massivə köçürməyə imkan verir. Faydalıdır, məsələn, köhnə API funksiyasına məlumat qaytarmaq lazımdırsa, hansı ki, yalnız massiv qəbul edir, və ya massivlə kütləvi emal aparmaq istəyirsənsə.
var contactsArray = new Contact[book.Count];
((ICollection<Contact>)book).CopyTo(contactsArray, 0);
// İndi contactsArray-dən köhnə üsulla istifadə edə bilərsən
8. Nümunə
Tədris konsol tətbiqimiz çərçivəsində, tutalım, biz ünvan kitabçası hazırlamağa başladıq. Deyək ki, Contact adlı sinfimiz var və ünvan kitabçası istənilən kolleksiya əsasında reallaşdırıla bilər, əsas odur ki, əlavə, silmə və iterasiya dəstəklənsin.
public class Contact
{
public string Name { get; set; }
public string Phone { get; set; }
}
public class AddressBook
{
private readonly ICollection<Contact> _contacts;
public AddressBook(ICollection<Contact> contacts)
{
_contacts = contacts;
}
public void AddContact(Contact contact)
{
_contacts.Add(contact);
}
public bool RemoveContact(Contact contact)
{
return _contacts.Remove(contact);
}
public int Count => _contacts.Count;
public void PrintAll()
{
foreach (var contact in _contacts)
{
Console.WriteLine($"{contact.Name} — {contact.Phone}");
}
}
}
İndi ünvan kitabçamız həm List<Contact>, həm də HashSet<Contact> ilə işləyə bilər, hətta — gələcəkdə — öz kolleksiyamızla, hansı ki, bazada və ya faylda reallaşdıracağıq! İstifadə nümunəsi:
var book = new AddressBook(new List<Contact>());
book.AddContact(new Contact { Name = "İvan", Phone = "+7-123-456" });
book.AddContact(new Contact { Name = "Mariya", Phone = "+7-987-654" });
Console.WriteLine($"Ümumi kontakt sayı: {book.Count}");
book.PrintAll();
Əgər təkrarlardan qurtulmaq istəyirik, sadəcə AddressBook-a siyahı əvəzinə çoxluq ötürürük:
var book = new AddressBook(new HashSet<Contact>());
(Contact düzgün müqayisə implementasiya etməlidir, amma bu barədə ayrıca dərs olacaq!)
9. Metodların xüsusiyyətləri və tipik səhvlər
Dərhal diqqət: ICollection<T>-ı implementasiya edən bütün kolleksiyalar eyni davranmır! Məsələn, kolleksiya IsReadOnly == true olduqda onu dəyişmək cəhdi istisna atacaq. HashSet<T> ilə işləyərkən Add metodu yalnız element həqiqətən əlavə olunubsa true qaytarır (əvvəl yox idisə). Bundan əlavə, müxtəlif kolleksiyalarda metodların sürəti fərqli ola bilər, — amma interfeys bunu tənzimləmir.
Daha bir yayılmış məqam: əgər kolleksiya bir neçə thread tərəfindən sinxronizasiya olunubsa, bir thread-də kolleksiyanı dəyişmək və digərində iterasiya etmək istisnalara səbəb ola bilər. Multithread üçün xüsusi kolleksiyalar lazımdır (ConcurrentBag<T>, ConcurrentQueue<T> və s. — bu barədə sonra danışacağıq).
Daha bir tələ: əgər kolleksiyanı CopyTo ilə kopyaladınsa, sonra massivdə və kolleksiyada dəyişikliklər bir-birindən asılı olmayacaq. Massiv — ayrıca yaddaş sahəsidir.
GO TO FULL VERSION