CodeGym /Kurslar /C# SELF /İnterfeys IComparable<T...

İnterfeys IComparable<T>

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

1. Giriş

Təsəvvür elə, dostundan kitabları rəflərə düzməsini xahiş edirsən. Əgər kitabların üzərində nömrələr varsa (1, 2, 3…), dostun bunu rahatlıqla edəcək: "Aha, 1 2-dən əvvəl gəlir, 2 3-dən əvvəl gəlir". Bu — ədədlərin təbii qaydasıdır (ardıcıllıqla). Eyni qayda ilə, əgər kitabların adları "A", "B", "V" hərfləri ilə başlayırsa, onları əlifba sırası ilə düzəcək. Bu da sətirlər üçün "təbii" qaydadır (əlifba ilə).

Bəs əgər kitabların üzərində sadəcə müəllifin soyadı yazılıbsa? Və sən deyirsən: "Qaydaya görə düz". Dostun soruşacaq: "Hansına görə? Soyadına? Çap ilinə? Səhifə sayına?" Bax, problem də burdan başlayır. Sənin unikal obyektlərin üçün aşkar təbii qayda yoxdur.

Proqramlaşdırmada da eyni məsələdir. Məsələn, tam ədədlər siyahısını List<int> sıralamaq istəyəndə, C# bunu necə edəcəyini yaxşı bilir. List<string> üçün də əlifba qaydasından istifadə edir. Amma əgər List<Student> varsa, burada hər Student — ad, soyad, yaş, ID və bir sürü başqa şey saxlayırsa, C# çaşır. Hansı xüsusiyyətə görə müqayisə etsin? Ada? ID-yə? Orta bala? Bu, "Kolleksiyaların sıralanması" mövzusunda rastlaşdığımız problemdir: List<T>.Sort() istifadə edəndə, öz obyektlərimizi sıralamaq istəyəndə səhv çıxır.

Bu tapmacanı həll etmək üçün C#-a aydın şəkildə deməliyik ki, obyektlərimizi hansı prinsipə görə müqayisə etmək lazımdır. Bunun üçün IComparable<T> interfeysi var.

2. IComparable<T> interfeysi

Deməli, obyektlərimizi "öz yerini" bilən etmək üçün IComparable<T> interfeysindən istifadə edirik. Bu — müqavilədir. Sənin class-ın və ya struct-ın bu interfeysi reallaşdıranda, sanki kompilyatora deyirsən: "Salam! Mənim obyektlərim bir-biri ilə müqayisə oluna bilər və bax belə etmək lazımdır."

Necə işləyir?

IComparable<T> interfeysi cəmi bir metod təyin edir:

public interface IComparable<in T>
{
    int CompareTo(T other);
}

Bu metod T tipində obyekt qəbul edir (yəni cari obyektin tipində) və qaytarmalıdır:

  • mənfi ədəd (< 0), əgər cari obyekt "kiçikdirsə";
  • sıfır (0), əgər "bərabərdirsə" (sıralama baxımından);
  • müsbət ədəd (> 0), əgər cari obyekt "böyükdürsə".

Əslində, bu idman hakimlərinə bənzəyir: məsələn, boks döyüşündə — əgər sən daha yaxşı idin, raundu udursan və daha çox xal alırsan; zəif idinsə — az xal; bərabərdirsə — xal eynidir. Burada isə hakimlər əvəzinə sadəcə işarəli ədəd var.

Niyə məhz belə?

Sıralama metodları (məsələn, List<T>.Sort()) siyahıdakı elementlər üçün CompareTo çağırır ki, hansını qabağa, hansını axıra qoymaq lazım olduğunu bilsin. Əgər class-ın bu interfeysi reallaşdırırsa — onu sıralamaq olar!

3. Praktika

Tutaq ki, belə bir istifadəçi class-ımız var (User):

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Gəlin istifadəçilər siyahısını sıralamağa çalışaq:

List<User> users = new List<User>
{
    new User { Name = "Sergey", Age = 31 },
    new User { Name = "Mariya", Age = 22 },
    new User { Name = "Anton", Age = 27 }
};

users.Sort(); // BOOM! InvalidOperationException

Səhv çıxır: "Ən azı bir obyekt IComparable reallaşdırmalıdır" (Ən azı bir obyekt IComparable reallaşdırmalıdır).

Düzəldirik: IComparable<User> reallaşdırırıq

Class-ımıza interfeys əlavə edirik. Gəlin əvvəlcə yaşa görə sıralayaq — kiçikdən böyüyə:

public class User : IComparable<User>
{
    public string Name { get; set; }
    public int Age { get; set; }

    public int CompareTo(User other)
    {
        // Axmaqlıqdan qorunma: əgər other == null, bizim istifadəçi "böyükdür"
        if (other == null) return 1;
        return this.Age.CompareTo(other.Age); // yaşa görə sıralayırıq
    }
}

İndi siyahı düzəldək, users.Sort(); çağırıb nəticəni konsolda çap edək:

List<User> users = new List<User>
{
    new User { Name = "Sergey", Age = 31 },
    new User { Name = "Mariya", Age = 22 },
    new User { Name = "Anton", Age = 27 }
};

// Yaşa görə sıralama (CompareTo istifadə edir)
users.Sort();

// Sıralanmış istifadəçiləri çıxışa veririk
foreach (User user in users)
{
    Console.WriteLine($"{user.Name}, {user.Age}");
}

Siyahı yaşa görə sıralanacaq:

Mariya, 22
Anton, 27
Sergey, 31

Vizualizasiya: əvvəl və sonra

Ad Yaş
Sergey 31
Mariya 22
Anton 27

Sıralamadan sonra:

Ad Yaş
Mariya 22
Anton 27
Sergey 31

4. Vacib detallar və tez-tez edilən səhvlər

null-dan qorunma

CompareTo içində çox vacibdir ki, other null-a bərabər deyilmi, yoxlayasan. Əgər bunu unutsaq, obyekt NullReferenceException ala bilər — onlar da kodda buraxılan bug-lar kimi hiyləgərdir. Adətən, müqayisə olunan obyekt null olsa, cari obyekt "böyük" sayılır:

public int CompareTo(User other)
{
    if (other == null) return 1;
    // ...
}

Tranzitivliyi qoruyun

Əgər A < B və B < C-dirsə, onda A < C olmalıdır. Əgər bu qaydaya əməl etməsən, sıralama gözlənilməz davranacaq (yəni, maraqlı, amma səhv!).

Əgər bir neçə sahəyə görə sıralamaq lazımdırsa

Tutaq ki, əvvəlcə yaşa görə, amma iki istifadəçi eyni yaşdadırsa, onları ada görə əlifba ilə sıralamaq lazımdır. Bu belə edilir:

public int CompareTo(User other)
{
    if (other == null) return 1;

    int ageCompare = this.Age.CompareTo(other.Age);
    if (ageCompare != 0) return ageCompare;

    // Əgər yaşlar bərabərdirsə — ada görə müqayisə edirik
    return this.Name.CompareTo(other.Name);
}

5. Sıralama: artıq sənin obyektlərinlə də

List<int> nə bacarırsa — sənin class-ın da bacarır

İndi müqayisə tələb edən istənilən metodu istifadə edə bilərsən: Sort, BinarySearch, hətta sıralanmış kolleksiyalara əlavə etmə (məsələn, SortedSet<T>).

users.Sort();
// users artıq yaşa görə (və yaş bərabərdirsə ada görə) sıralanıb

Kurs tətbiqində nümunə

Tutaq ki, əvvəllər istifadəçi uçotu üçün tətbiq yazmışdın. İndi onlara "təbii" sıralama qaydası əlavə edə bilərik. Bax belə görünəcək:

// Bizim User class-ı artıq IComparable<User> reallaşdırır

List<User> users = new List<User>
{
    new User { Name = "İvan", Age = 45 },
    new User { Name = "Qalina", Age = 27 },
    new User { Name = "Yuri", Age = 27 }
};

// Əvvəl yaşa, sonra ada görə sıralayırıq
users.Sort();
foreach (var u in users)
{
    Console.WriteLine($"{u.Name} - {u.Age}");
}

Nəticə:

Qalina - 27
Yuri - 27
İvan - 45

Qalina və Yuri — eyni yaşdadır, yaş bərabərdirsə ada görə sıralanır.

6. CompareTo necə işləyir: sərt riyaziyyat

Gəlin bir daha qaytarılan dəyərlərin standartına diqqət yetirək:

  • Mənfi ədəd (məsələn, -1): cari obyekt müqayisə olunanın qabağında gedir.
  • Sıfır: sıralama baxımından bərabər sayılırlar.
  • Müsbət ədəd (məsələn, 1): cari obyekt müqayisə olunanın arxasında gedir.

Daxili tiplər (məsələn, Age.CompareTo(other.Age)) artıq bu standartı reallaşdırır, həmişə -1, 0 və ya 1 qaytarır.

CompareTo metodu üçün qaytarma cədvəli

Qaytarılan dəyər Nə deməkdir? Nümunə
< 0 Kiçikdir (qabaqda gedir)
20.CompareTo(21): -1
0 Bərabərdir
23.CompareTo(23): 0
> 0 Böyükdür (sonra gedir)
42.CompareTo(15): 1

7. Çoxsahəli sıralama: sahələri birləşdiririk

Bəzən daha mürəkkəb sıralama lazımdır: məsələn, soyad, ad və yaşa görə. Artıq tanış olduğumuz üsulla, ardıcıl müqayisə edə bilərik:

public class Student : IComparable<Student>
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public int Grade { get; set; }

    public int CompareTo(Student other)
    {
        if (other == null) return 1;
        int lastNameCompare = this.LastName.CompareTo(other.LastName);
        if (lastNameCompare != 0) return lastNameCompare;
        int firstNameCompare = this.FirstName.CompareTo(other.FirstName);
        if (firstNameCompare != 0) return firstNameCompare;
        return this.Grade.CompareTo(other.Grade);
    }
}

8. IComparable<T> reallaşdırmaq lazım olmayan hallar

Həyatdan (proqramçı həyatından!) bir vəziyyət: əgər obyektin "təbii" sıralama qaydası yoxdursa, IComparable<T> reallaşdırmaq yaxşı deyil. Məsələn, Point class-ın varsa, amma bilmirik — X-ə, Y-yə, yoxsa koordinat başlanğıcından məsafəyə görə sıralamaq lazımdır — onda sıralama xaricdən, müqayisə funksiyası ilə (IComparer<T>) verilsin. Bu barədə növbəti mühazirədə danışacağıq.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION