1. Kolleksiyalara interfeys niyə lazımdır?
Düzünü desək: biz kodu konkret tipdə (List<int>, Dictionary<string, string>) yazanda hər şey rahat və başadüşülən idi. Amma təsəvvür elə ki, birdən List<T> əvəzinə HashSet<T> istifadə etmək istədin, çünki sənə yalnız unikal dəyərlər lazımdır. Və ya elə bir metod yazmaq istəyirsən ki, həm bir tip, həm də başqa tip kolleksiya ilə işləsin. Necə olsun?
Bir giriş nöqtəsi yaratmaq lazımdır — yəni "universal rozetka". C#-da bu rolu interfeyslər oynayır. Onlar kolleksiyanın malik olmalı olduğu qaydalar (metodlar, xassələr) dəstini müəyyən edir. Hər nə interfeysi reallaşdırırsa — göstərilən əməliyyatları dəstəkləyir.
Bu, telefon üçün adapter kimidir: əgər standart USB-C rozetkan varsa, həm telefonu, həm müasir tozsoranı, hətta elektrik diş fırçasını da şarj edə bilərsən (əsas odur ki, qaranlıqda onları səhv salmayasan).
Praktik nümunə
Tutaq ki, istədin elə bir metod yazasan ki, istənilən ədədlər kolleksiyasındakı bütün ədədlərin cəmini hesablasın. İnterfeys olmadan bu belə olardı:
// Bu metod yalnız massivlə işləyir:
static int SumArray(int[] array)
{
int sum = 0;
for (int i = 0; i < array.Length; i++)
{
sum += array[i];
}
return sum;
}
// Bu isə yalnız List<int> ilə:
static int SumList(List<int> list)
{
int sum = 0;
for (int i = 0; i < list.Count; i++)
sum += list[i];
return sum;
}
Rahat deyil! Axı kod demək olar ki, eynidir. IEnumerable<int> interfeysi ilə bunu universal eləmək olar:
static int SumAll(IEnumerable<int> collection)
{
int sum = 0;
foreach (var number in collection)
sum += number;
return sum;
}
İndi bu metod istənilən kolleksiya tipini qəbul edir ki, o IEnumerable<int> reallaşdırır: massiv, siyahı, set və s.
2. Kolleksiyalar üçün əsas interfeyslər
.NET-də kolleksiyalar üçün bir neçə əsas interfeys var. Gəlin qısa baxaq — növbəti mühazirələrdə hər birinin detallı reallaşdırmasını öyrənəcəksən.
| İnterfeys | Nəyə lazımdır | İnterfeysi reallaşdıran kolleksiya nümunələri |
|---|---|---|
|
Elementləri sadalamaq (foreach) | Hamısı: , , massivlər |
|
Dəyişən dəst (Add, Remove, Count) | , , |
|
İndeks üzrə giriş, dəyişmək | , massivlər |
|
"Açar-dəyər" cütləri ilə işləmək | , |
|
Set (unikal elementlər) | , |
Qeyd: Sadəlik üçün hələlik bu interfeyslər arasındakı "bir neçə səviyyə" irsiyyəti keçirik. Kim kimdən miras alır — bunu bir az sonra danışacağıq.
Vizualizasiya: kolleksiya interfeyslərinin ağacı
IEnumerable<T>
^
|
ICollection<T>
^ ^
| |
IList<T> ISet<T>
| |
List<T> HashSet<T>
Sözlüklər üçün:
IEnumerable<KeyValuePair<K,V>>
^
|
ICollection<KeyValuePair<K,V>>
^
|
IDictionary<K,V>
|
Dictionary<K,V>
3. Real həyatda interfeyslər niyə lazımdır?
Elastiklik
Sənin metodun və ya klassın konkret tip (List<int>) yox, interfeys (IEnumerable<int>) ilə işləyəndə, ona bu interfeysi reallaşdıran istənilən kolleksiyanı ötürə bilərsən. Nəticədə proqram elastik və genişlənən olur. ("Yalnız iPhone" əvəzinə — indi istənilən Bluetooth-lu telefon uyğundur.)
static void PrintAll(IEnumerable<string> collection)
{
foreach (var item in collection)
Console.WriteLine(item);
}
İndi istənilən siyahı, massiv, set, hətta LINQ sorğusunun nəticəsini çap edə bilərsən!
Unifikasiya
Əgər öz kolleksiya tipini reallaşdırırsansa (məsələn, hansısa xüsusi tapşırıq üçün spesifik kolleksiya), lazım olan interfeysləri reallaşdır — və kolleksiyan sənin kolleksiyanı tanımayan başqa kodla işləyəcək. Ona görə də .NET-də tez-tez yazırlar: "interfeys səviyyəsində proqramlaşdır, konkret reallaşdırmada yox".
Standart alətlərlə uyğunluq
Standart interfeysləri dəstəklədiyinə görə, kolleksiyaların LINQ, sortlama, serializasiya və .NET-in başqa imkanları ilə işləyə bilər.
4. List<T> — həm siyahı, həm kolleksiya, həm də sadalama
Sadalama haqqında bir neçə söz
IEnumerable<T> — .NET-də kolleksiyaların ən əsas və ən baza interfeysidir. O, yalnız bir metod müəyyən edir — GetEnumerator(), hansı ki, kolleksiya üzrə iterasiya üçün enumerator obyektini qaytarır. Məhz buna görə belə yaza bilirsən:
foreach (var name in names)
{
Console.WriteLine(name);
}
Burada names — həm siyahı, həm set, hətta massiv ola bilər. foreach — bu, arxada GetEnumerator() metodunu istifadə edən ağıllı sintaksis şəkəridir.
IEnumerable interfeysi (<T> olmadan) — daha köhnə, generic olmayan versiyadır. İndi demək olar ki, həmişə generic versiyanı istifadə et.
List klassının elan sintaksisi
Gəlin List<T> klassının elanına baxaq, bir az sehrini açsaq:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, ...
Bu o deməkdir ki, List<T> obyektini belə istifadə edə bilərsən:
- İndeks üzrə istənilən girişli siyahı kimi (IList<T>)
- Element əlavə/silmək imkanı olan kolleksiya kimi (ICollection<T>)
- Sadəcə elementlər dəsti kimi, üzərində gəzə biləcəyin (IEnumerable<T>)
Nümunə:
List<string> names = new List<string> { "Anna", "Boris", "Vasya" };
// IEnumerable kimi - dövrdə gəzə bilərsən
IEnumerable<string> asEnumerable = names;
foreach (var n in asEnumerable) Console.WriteLine(n);
// ICollection kimi - Count-u öyrənə bilərsən:
ICollection<string> asCollection = names;
int count = asCollection.Count;
// IList kimi - indekslə müraciət edə bilərsən:
IList<string> asList = names;
string firstItem = asList[0];
5. Tətbiq nümunəsi
Tutaq ki, bizdə istifadəçilər və onların rolları üçün sadə bir tətbiq var (biz bunu keçən mühazirələrdə başlamışdıq):
// İstifadəçi klası
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
İstifadəçiləri istənilən IEnumerable<User> reallaşdıran kolleksiyada saxlaya bilərik:
List<User> listUsers = new List<User> { new User { Name = "Anna", Age = 20 } };
HashSet<User> setUsers = new HashSet<User> { new User { Name = "Boris", Age = 25 } };
// Bütün istifadəçi adlarını çap edən metod
static void PrintUserNames(IEnumerable<User> users)
{
foreach (var user in users)
Console.WriteLine(user.Name);
}
// Fərqli kolleksiya tipləri ilə istifadə edirik:
PrintUserNames(listUsers);
PrintUserNames(setUsers);
İndi tətbiq daha elastik olur — heç bir magiya və ya xüsusi overload-lar olmadan!
6. Tipik səhvlər və tələlər
Ən çox rast gəlinən səhvlərdən biri — interfeys kifayət edəndə çox konkret tipdən istifadə etməkdir:
// Pis (reallaşdırmaya bərk bağlanmısan)
void DoSomethingWithList(List<int> numbers) { ... }
// Daha yaxşı (metod istənilən kolleksiya ilə işləyir)
void DoSomethingWithNumbers(IEnumerable<int> numbers) { ... }
Başqa bir tələ — unutmaq ki, interfeys yalnız onda açıq göstərilən əməliyyatları müəyyən edir. Məsələn, IEnumerable<T> ilə elementlərin sayını (Count) öyrənə bilməzsən, bunun üçün ICollection<T> istifadə etməlisən — ya da əl ilə saymalısan.
GO TO FULL VERSION