1. Klassik fərqlər qısa şəkildə
Kimsə deyirsə: «İnterfeys — sadəcə signaturaların toplusudur», soruş: «Sən hansı C# versiyasında yazırsan?» Artıq C# 8-dən başlayaraq interfeyslər xeyli güclənib. Gəlin onları abstrakt siniflərlə müqayisə edək — təkcə klassik xüsusiyyətlərə görə yox, həm də .NET platformasının bütün yeni fəndlərini nəzərə alaraq.
Bir neçə il əvvələ — C# 7-yə qayıtsaq, hər şey sadə idi. Abstrakt sinif sahələr və qismən reallaşdırılmış metodlar təyin edə bilərdi, interfeys isə yalnız signaturalar (metodlar, propertilər, eventlər, indeksatorlar).
Abstrakt sinifdən irsi almaq "is-a" (Is-a) münasibəti ilə olurdu, interfeyslər isə davranışın çoxlu irsiliyini təmin edirdi ("can-do", yəni "bacarır").
| Xüsusiyyət | Abstrakt sinif | İnterfeys (C# 8-dək) |
|---|---|---|
| Münasibət | is-a | can-do |
| İrsi alma | Yalnız bir | Çoxlu |
| Sahələr | Ola bilər | Ola bilməz |
| Metodların reallaşdırılması | Ola bilər | Ola bilməz |
| Konstruktorlar | Ola bilər | Ola bilməz |
| Giriş modifikatorları | Müxtəlif (public, protected, ...) | Yalnız qeyri-aşkar public |
Gördüyün kimi, əvvəllər abstrakt siniflər interfeyslərin "böyük qardaşı" idi — daha güclü və çevik. Amma hər şey dəyişir!
2. Default implementasiyalı interfeyslər
C# 8 çıxandan (və əlbəttə C# 14 və .NET 9-da) interfeyslər yeni superqabiliyyət qazandı — default implementasiyalı metodlar, yəni "Default Interface Methods" (DIM).
Necə görünür?
public interface IAnimal
{
void SayHello();
// Default implementasiyalı metod!
void Walk()
{
Console.WriteLine("Mən gedirəm...");
}
}
Vau! Birdən məlum oldu ki, interfeys artıq metodun reallaşdırmasını saxlaya bilər. Həm də bir yox, istədiyin qədər. Amma bir nüans var: belə metodlar mütləq bədənlə açıq şəkildə elan olunmalıdır, qalan hər şey (sahələr, private metodlar, konstruktorlar) — hələ də olmaz.
3. Müasir interfeys imkanları
Müasir .NET developerin bilməli olduğu yeni fəndlər:
- Default implementasiyalı metodlar.
- İnterfeysdə private metodlar (yalnız köməkçi məqsədlər üçün, yalnız həmin interfeysin digər metodlarına açıqdır).
- Statik metodlar (C# 8-dən başlayaraq).
- Default implementasiyalı propertilər.
- Statik sahələr (C# 14-dən — "static interface members").
- Abstrakt statik üzvlər ("abstract static members" — bəli, artıq interfeys implementasiyadan müəyyən statik metodlar tələb edə bilər!).
Müasir interfeysin tam nümunəsi:
public interface ILogger
{
static int LoggerCount { get; set; } // C# 14
void Log(string message); // Signatura (kontrakt)
// Default implementasiya
void LogWarning(string warning)
{
Log("[WARNING]: " + warning);
}
// İnterfeysdə private köməkçi metod (C# 8+)
private void FormatAndLog(string level, string msg)
{
Log($"{level}: {msg}");
}
// İnterfeysdə static metod (C# 8+)
static void PrintLoggerInfo()
{
Console.WriteLine("ILogger interfeysi — ən yaxşı köməkçinizdir!");
}
}
Sadəcə təsəvvür elə — əvvəllər bu mümkün deyildi, elə bil pişiyə server mühafizəçisi işləməyə icazə veriblər.
4. Abstrakt siniflər: nə yenilik var?
Abstrakt siniflər... necə deyim... son on ildə elə də çox dəyişməyib. Onlar hələ də aşağıdakıları saxlaya bilər:
- Sahələr (private, protected və static daxil olmaqla).
- Reallaşdırılmış və abstrakt metodlar.
- Konstruktorlar (bəli, abstrakt sinifdə init loqikası ola bilər).
- Propertilər, eventlər, indeksatorlar.
- Statik və instans üzvlər.
Abstrakt sinif nümunəsi:
public abstract class Animal
{
public string Name { get; set; }
public abstract void Speak();
public virtual void Walk()
{
Console.WriteLine($"{Name} pəncələri ilə addımlayır!");
}
protected void Eat()
{
Console.WriteLine($"{Name} yem yeyir.");
}
}
Abstrakt sinif hələ də ümumi loqika, vəziyyət və davranış üçün sinif iyerarxiyası üçün əla yerdir.
5. Müasir müqayisə: yeni imkanlarla cədvəl
| Xüsusiyyət | Abstrakt sinif | İnterfeys (C# 14+, .NET 9) |
|---|---|---|
| Münasibət | is-a (yəni) | can-do (bacarır) |
| İrsi alma | Yalnız bir | Çoxlu |
| Sahələr | Bəli, istənilən | Yalnız statik* (C# 14+) |
| Konstruktorlar | Bəli | Xeyr |
| Metodların reallaşdırılması | Bəli (virtual/abstract) | Bəli (default, static, abstract static) |
| Default implementasiyalı propertilər | Bəli | Bəli (default implementasiya) |
| Private üzvlər | Bəli | Bəli (yalnız metodlar, C# 8+) |
| Statik üzvlər | Bəli | Bəli (C# 8+, məhdudiyyətlərlə) |
| Statik sahələr | Bəli | Bəli * (C# 14+) |
| Giriş modifikatorları | İstənilən | Default olaraq public və ya private |
* — İnterfeyslərdə statik sahələr adətən xüsusi hallarda istifadə olunur və bu, dilin tam yeni fəndidir.
6. Harada interfeys, harada abstrakt sinif: müasir tövsiyələr
İnterfeyslər (indi artıq default implementasiyalı) — komponentlər arasında kontrakt yaratmaq üçün alətdir. Əsas xüsusiyyəti: çoxluq. Sənin sinifin istədiyin qədər interfeys implementasiya edə bilər, bu da onu universal əsgər edir.
Abstrakt sinif seçimin olur, əgər:
- Ümumi vəziyyət (sahələr), loqika və davranış lazımdır ki, digər siniflər irs alsın.
- Standart, amma override oluna bilən loqika lazımdır (virtual istifadə et).
- İnitializasiya üçün konstruktor vasitəsilə mərkəzləşdirmək istəyirsən.
Real layihələrdə tez-tez belə sxem olur: "təmiz kontraktlar" interfeyslərdə yazılır, əgər ümumi kod və ya infrastruktur lazımdırsa — abstrakt baza sinifi yaradılır.
. ┌────────────────────────┐
│ İnterfeys │
│ (kontrakt: nə bacarır)│
└─────────┬──────────────┘
│
┌──────────────┼──────────────┐
│ │ │
İmplementasiya 1 İmplementasiya 2 ... İmplementasiya N
MyLogger CloudLogger FileLogger
(abstrakt sinifdən irsi alma ilə kombinə oluna bilər)
7. Ssenarilər — nə vaxt nə üstünlük verir
Çoxlu implementasiya:
Tutaq ki, səndə IDrivable interfeysi və Vehicle abstrakt sinifi var. İndi Car sinifi baza kimi Vehicle-dan irsi ala və eyni zamanda bir neçə interfeysi (IDrivable, IRepairable, IInsurable) implementasiya edə bilər. Əgər səndə Repairable abstrakt sinifi olsaydı, seçim etməli olardın — ya Vehicle, ya Repairable! Burada interfeyslər açıq-aşkar qalibdir.
Ümumi loqika və vəziyyət:
Tutaq ki, bütün "avtonəqliyyat"da "nömrə" sahəsi var. Bu, abstrakt sinifdə sahə olmalıdır. İnterfeysdə sahə (statikdən başqa) mümkün deyil.
API-nin təkamülü:
Default Interface Methods ilə inqilabi dəyişiklik — artıq interfeysləri təkamül etdirmək olar, mövcud istifadəçiləri sındırmadan.
Məsələn, interfeysə yeni default implementasiyalı metod əlavə etdin — hər şey işləyir, bütün köhnə interfeys implementasiyaları düşmür! Əvvəllər bu ağrılı idi (və ya ağrını unutsan belə, mümkün deyildi).
8. Praktik nümunələr
Tədris tətbiqimizdə tədricən loglama yaranır. Gəlin öz ILogger interfeysimizi default implementasiya ilə yaradaq:
public interface ILogger
{
void Log(string message);
// Default implementasiya bütün interfeys implementasiyalarına açıqdır!
void LogInfo(string info)
{
Log("[INFO] " + info);
}
// İnterfeysin statik metodu
static void PrintHelp()
{
Console.WriteLine("Hadisələrin loglanması üçün ILogger istifadə edin");
}
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
// Kodda haradasa:
ILogger logger = new ConsoleLogger();
logger.LogInfo("Sistem işə düşdü!"); // default implementasiya sayəsində işləyir
// İnterfeysin statik metodunu çağırmaq
ILogger.PrintHelp();
Əgər interfeysə yeni default implementasiyalı metod əlavə etsəydik, bütün mövcud implementasiyalar (məsələn, ConsoleLogger) avtomatik olaraq bu yeni metodu alardı — heç bir panika və kodun sınması yoxdur.
9. Səhvlər və nüanslar: praktika və tələlər
Bilmək lazımdır ki, hər şey göründüyü qədər rəngarəng deyil. Məsələn, əgər interfeysin default implementasiyası var, amma istifadəçi obyektə sinif tipi ilə yox, interfeys tipi ilə müraciət edirsə, default implementasiya yalnız interfeys vasitəsilə əlçatandır.
ConsoleLogger log = new ConsoleLogger();
log.LogInfo("Salam"); // Kompilyasiya olunmayacaq: LogInfo sinifdə müəyyən olunmayıb!
ILogger log2 = log;
log2.LogInfo("Salam"); // Hər şey əladır!
Bu, interfeysin explicit implementasiyasının xüsusi halına bənzəyir. Bəzən belə davranış artıq API-ni gizlətmək üçün rahatdır, bəzən isə yeni başlayanlar üçün gözlənilməz olur.
GO TO FULL VERSION