CodeGym /Kurslar /C# SELF /C# kolleksiyalarında interfeyslərin rolu

C# kolleksiyalarında interfeyslərin rolu

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

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
IEnumerable<T>
Elementləri sadalamaq (foreach) Hamısı:
List<T>
,
HashSet<T>
, massivlər
ICollection<T>
Dəyişən dəst (Add, Remove, Count)
List<T>
,
HashSet<T>
,
Dictionary<K,V>
IList<T>
İndeks üzrə giriş, dəyişmək
List<T>
, massivlər
IDictionary<K,V>
"Açar-dəyər" cütləri ilə işləmək
Dictionary<K,V>
,
SortedDictionary<K,V>
ISet<T>
Set (unikal elementlər)
HashSet<T>
,
SortedSet<T>

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>
Siyahı və set kolleksiyaları üçün interfeys iyerarxiyası

Sözlüklər üçün:


           IEnumerable<KeyValuePair<K,V>>
                      ^
                      |
               ICollection<KeyValuePair<K,V>>
                      ^
                      |
               IDictionary<K,V>
                      |
                Dictionary<K,V>
Sözlüklər üçün interfeys iyerarxiyası

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.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Öz kolleksiyan üçün IEnumerable interfeysinin reallaşdırılması
Öz kolleksiyan üçün IEnumerable interfeysinin reallaşdırılması
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION