1. Əsas interfeyslərin təsnifatı
Əgər düşünürsən ki, interfeyslər — bu, arxitektorlar və "təmiz kod" üçün oyuncaqdır və real işdə onlarsız keçinmək olar, səni təəccübləndirim: C#-da hər hansı ciddi layihə interfeyslərə başdan-ayağa dalır. Niyə? Çünki .NET standart kitabxanasının demək olar ki, hər bir hissəsi interfeyslərlə qurulub! Onlarsız nə kolleksiyalarla işləyə, nə fayl oxuya, hətta LINQ ilə kolleksiyaları filtrləyə bilməzsən.
İnterfeyslər — .NET-də polimorfizm və genişlənmənin təməlidir və məhz onlar bütün framework-un "kublarının" bir-birinə necə birləşdiyini müəyyən edir.
.NET standart kitabxanası sözün əsl mənasında interfeyslərlə doludur. Bu dənizdə batmamaq üçün belə bir təsnifat təklif edirəm (əlbəttə, bu tam deyil — hər şeyi əhatə etməyə ömür çatmaz!):
| Kateqoriya | İnterfeyslər | Nəyə lazımdır? |
|---|---|---|
| Kolleksiyalar | , , , , |
Dolaşmaq, dəyişmək, indekslə giriş, açar-dəyər cütləri ilə işləmək |
| Resurslarla işləmək | |
Resursların azad olunması (fayllar, bağlantılar, axınlar) |
| Müqayisə | , , |
Sıralama, müqayisə, obyektlərin unikalığı |
| Seriyallaşdırma | |
Obyektləri bayt axınına çevirmək (və əksinə) |
| LINQ və sorğular | , |
Mürəkkəb sorğuların dəstəyi (məsələn, DB-yə) |
| Asinxronluq | , |
Asinxron dolaşma və resursların təmizlənməsi |
| Hadisələr və bildirişlər | , |
Xüsusiyyətlərin/kolleksiyaların dəyişməsinə reaksiya |
| Tarix və vaxt | |
Sətirlərin xüsusi formatlanması |
| Verilənlər strukturları | , |
Kolleksiya və tuple-ların dərin müqayisəsi |
| Axınlar/giriş-çıxış | , , , |
Axınlarla işləmək, push/pull-bildirişlər |
Vacibdir! Yuxarıda sadalanan interfeyslərin bir çoxunun tip-parametrləri var: List<string>. Adətən bunlar kolleksiyalarda və kolleksiya interfeysləri üçün istifadə olunur Int[] → List<int>. Onları daha ətraflı bir neçə səviyyə sonra, kolleksiyalarla tanış olanda müzakirə edəcəyik.
2. Kolleksiya interfeysləri
IEnumerable və IEnumerator — foreach üçün keçid biletin
.NET-də demək olar ki, hər bir kolleksiya IEnumerable və ya onun generic versiyası IEnumerable<T> interfeysini reallaşdırır. Məhz bu interfeys sənə foreach sehrli sintaksisini işlətməyə imkan verir:
List<int> numbers = new List<int> { 1, 2, 3 };
foreach (int n in numbers) // işləyir, çünki List<int> IEnumerable<int> reallaşdırır
{
Console.WriteLine(n);
}
Bu interfeys minimal versiyada belə görünür:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
IEnumerator — kolleksiyanı "gəzən" interfeysdir, kolleksiyanı addım-addım gəzir:
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
Real tapşırıqlarda sən çox tez-tez parametr kimi və ya qaytarılan dəyər kimi IEnumerable<T> istifadə edəcəksən. Məsələn, massivdən bütün cüt ədədləri qaytaran metod:
public IEnumerable<int> GetEvenNumbers(int[] array)
{
foreach (var x in array)
if (x % 2 == 0) yield return x;
}
Bəli, yield açar sözü magiya edir — interfeysi sənin əvəzinə reallaşdırır, amma bu barədə sonra daha ətraflı danışacağıq.
ICollection və IList — indekslə giriş və dəyişiklik imkanı olan kolleksiyalar
Əgər sənə sadəcə dolaşmaq yox, element əlavə/çıxarmaq və ya indekslə əldə etmək lazımdırsa, daha ixtisaslaşmış interfeyslər lazımdır:
- ICollection<T> — Add, Remove metodlarını və Count xüsusiyyətini əlavə edir.
- IList<T> — əməliyyatların siyahısını genişləndirir, o cümlədən indekslə giriş (this[int index]).
public void PrintFirstItem(IList<string> list)
{
if (list.Count > 0)
Console.WriteLine(list[0]);
}
List<T> həm IEnumerable<T>, həm ICollection<T>, həm də IList<T> reallaşdırır. Bu çox rahatdır — onunla həm sadə siyahı kimi, həm də bütün imkanlarla işləyə bilərsən.
IDictionary<TKey, TValue> — "açar-dəyər" cütləri
Əgər sən sözlüklərlə işləməyi sevirsənsə (və bu çox tez-tez olur!), IDictionary<TKey, TValue> interfeysindən istifadə et. O, sənə açarla dəyəri tapmağa və əksinə imkan verir.
public void PrintAllPairs(IDictionary<string, int> ages)
{
foreach (var pair in ages)
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
Burada ages istənilən şey ola bilər, istər Dictionary<string, int>, istər SortedDictionary<string, int> — əsas odur ki, bu interfeysi reallaşdırırlar!
3. IDisposable interfeysi: resurslarla düzgün işləmək
Bütün giriş-çıxış, fayllarla, şəbəkə bağlantıları, verilənlər bazası ilə işləmək .NET-də IDisposable interfeysinə bağlıdır. Bu interfeys həyati vacib müqaviləni müəyyən edir: əgər obyektin idarə olunmayan resursları varsa, istifadə bitəndən sonra onu "təmizləmək" lazımdır. Bəli, bu, using sintaksisini mümkün edən oğlandır:
using (StreamReader reader = new StreamReader("file.txt"))
{
// Faylla işləyirik, using-dən sonra fayl mütləq bağlanır!
string line = reader.ReadLine();
}
İnterfeysin özü necə qurulub? Gülməli dərəcədə sadədir:
public interface IDisposable
{
void Dispose();
}
Amma hər bir "ciddi" kitabxana onu reallaşdırır! Bu interfeyslə işləməyin yaxşı təcrübələri barədə daha ətraflı rəsmi sənəddə oxu.
4. Müqayisə və unikalıq üçün interfeyslər
IComparable<T> və IComparer<T>
Əgər istəyirsən ki, obyektlərinin kolleksiyası sıralana bilsin, obyektlər bir-biri ilə müqayisə edə bilməlidir. Bunun üçün IComparable<T> interfeysi var:
public class Student : IComparable<Student>
{
public string Name { get; set; }
public int Score { get; set; }
public int CompareTo(Student? other)
{
// Ballara görə azalan sıralama
if (other == null) return 1;
return other.Score.CompareTo(this.Score);
}
}
// İndi tələbələri sıralamaq olar:
var students = new List<Student> { ... };
students.Sort();
Daha ətraflı Microsoft sənədində.
IComparer<T> isə müqayisəni sinifdən kənarda müəyyən etməyə imkan verir — məsələn, bəzən tələbələri ada, bəzən ballara görə sıralamaq istəyirsən:
public class NameComparer : IComparer<Student>
{
public int Compare(Student? x, Student? y)
{
return string.Compare(x?.Name, y?.Name);
}
}
IEquatable<T> — bərabərlik üçün müqayisə
Obyektinin HashSet<T>-də (yəni kolleksiyalar üçün "unikal" olması) iştirak etməsini istəyirsənsə, IEquatable<T> reallaşdır:
public class Person : IEquatable<Person>
{
public string Name { get; set; }
public bool Equals(Person? other)
{
if (other == null) return false;
return this.Name == other.Name;
}
}
5. Hadisələr və bildirişlər üçün interfeyslər
Heç istəmisən ki, proqramın "bilsin" obyektin xüsusiyyətləri dəyişib? Məsələn, istəyirsən ki, interfeys özü yenilənsin, istifadəçi soyadını dəyişəndə? Məhz bunun üçün INotifyPropertyChanged interfeysi var. O, qrafik interfeysli tətbiqlərdə (məsələn, WPF və ya Xamarin) çox populyardır.
İnterfeysin imzası sadədir:
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler? PropertyChanged;
}
Reallaşdırma nümunəsi:
public class User : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private string name;
public string Name
{
get => name;
set
{
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
}
İndi bu obyektə istənilən UI bağlaması Name dəyişəndə avtomatik yenilənəcək. Əgər maraqlansan — rəsmi sənədə bax.
6. Digər faydalı interfeyslər
IFormattable interfeysi: çevik formatlama
Obyektinin sətirə gözəl və müxtəlif şəkildə çıxmasını (məsələn, vergüldən sonra fərqli sayda rəqəmlə) istəyirsənsə, IFormattable interfeysini reallaşdır:
public class Temperature : IFormattable
{
public double Celsius { get; }
public Temperature(double celsius) => Celsius = celsius;
public string ToString(string? format, IFormatProvider? formatProvider)
{
// Çətinləşdirməyək, sadəcə formata görə 'C' və ya 'F' göstərək
if (format == "F")
return $"{Celsius * 9 / 5 + 32} F";
return $"{Celsius} C";
}
}
İndi sən temp.ToString("F", null) və ya temp.ToString("C", null) çağırışını edə bilərsən. Buna görə DateTime, double, decimal və digər daxili tiplər çevik formatlamanı dəstəkləyir.
Asinxronluq üçün interfeyslər
C#-da asinxronluq gələndə asinxron dünyada işləmək üçün yeni interfeyslər lazım oldu. Məsələn, obyektin asinxron resursları azad edirsə — IAsyncDisposable reallaşdır:
public interface IAsyncDisposable
{
ValueTask DisposeAsync();
}
Və indi using əvəzinə await using yazırsan!
Asinxron kolleksiyalarla (await foreach) IAsyncEnumerable<T> interfeysi işləyir, hansı ki, elementləri əsas axını bloklamadan dolaşmağa imkan verir. Onu, məsələn, böyük faylları hissə-hissə oxuyanda və ya internetdən axınla məlumat alanda istifadə edirlər. Daha ətraflı 58-ci səviyyədə :P
Seriyallaşdırma interfeysləri: ISerializable
Tapşırığın obyektləri bayt axınına çevirməkdirsə (məsələn, saxlamaq və ya şəbəkə ilə ötürmək üçün), .NET ISerializable interfeysini təklif edir. Bu, indi o qədər də tez-tez istifadə olunmur, çünki daha rahat mexanizmlər var (məsələn, atributlar və hazır serializer-lər vasitəsilə), amma qeyd etməyə dəyər. İmza nümunəsi:
public interface ISerializable
{
void GetObjectData(SerializationInfo info, StreamingContext context);
}
7. İnterfeyslərin praktikada tətbiqi: real tətbiq
Tutaq ki, sən kitabxanada kitabların uçotu üçün konsol tətbiqi yazırsan (nə olsun, kimsə etməlidir!). İstəyirsən ki, kitab siyahılarını çıxara, sıralaya, filtrləyə, məlumatları yükləyə və saxlaya biləsən. .NET interfeyslərini bildiyinə görə sən:
- Fərqli kolleksiya tiplərini IEnumerable<Book> vasitəsilə qaytara bilərsən, istifadəçi düşünməsin ki, bu massivdir, yoxsa siyahı.
- Fərqli meyarlara görə sıralama əlavə edə bilərsən: IComparable<Book> başlıq üzrə sıralama üçün, ayrıca IComparer<Book> müəllifə görə.
- Fayllarla işləyərkən resursların effektiv azad olunmasını təmin edə bilərsən IDisposable vasitəsilə.
- Kitabları janra və ya müəllifə görə filtrləməyi təşkil edə bilərsən LINQ vasitəsilə, hansı ki, IEnumerable<T> reallaşdıran hər şeylə işləyir.
- Tətbiqi genişləndirə bilərsən — məsələn, fayl saxlama əvəzinə verilənlər bazasına keçə bilərsən, əgər siniflərin interfeyslərlə (IBookStorage) işləyirsə, konkret reallaşmalarla yox.
8. Tipik səhvlər və xüsusiyyətlər
Yeni başlayanların ən yayılmış səhvlərindən biri — dəyişən və metod arqumentlərini interfeyslərlə ("IEnumerable", "IList") yox, konkret reallaşmalarla ("List") elan etməkdir. YADDA SAXLA: əgər "interfeyslərlə proqramlaşdırırsansa", kodun çevik və asan genişlənən olacaq.
Bəzən hansı interfeys səviyyəsini seçəcəyinə diqqət etmək lazımdır — məsələn, metoda sadəcə dolaşmaq lazımdırsa, IEnumerable<T> istifadə et, IList<T> yox, artıq məhdudiyyət qoymamaq üçün.
Daha bir incəlik: əgər sinifdə çoxlu interfeys varsa və onların arasında eyni adlı metod və ya xüsusiyyətlər varsa, interfeysi açıq şəkildə (explicit implementation) reallaşdırmaq lazım olacaq, yoxsa kompilyator başa düşməyəcək.
GO TO FULL VERSION