CodeGym /Kurslar /C# SELF /C#-da interfeysin açıq (explicit) reallaşdırılması

C#-da interfeysin açıq (explicit) reallaşdırılması

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

1. Giriş

Adətən, sən interfeysi reallaşdıranda, sadəcə öz class-ında interfeysə uyğun public metodlar yaradırsan. Buna gizli və ya ümumi (public) reallaşdırma deyirlər. Compiler kifayət qədər ağıllıdır ki, başa düşsün: "Aha, bu DoSomething() metodu MyClass-da IDoable.DoSomething() üçün nəzərdə tutulub!".

Amma bəs belə olsa:

  • Sənin SmartDevice class-ın ICamera interfeysini reallaşdırır (onda TakePicture() metodu var) və IScreen interfeysini də reallaşdırır (onda da TakePicture() metodu var — screenshot üçün)?
  • Yaxud sənin Robot class-ında artıq public Reset() metodu var, hansı ki, bütün sistemləri sıfırlayır, amma sən istəyirsən ki, o həm də IDevice interfeysini reallaşdırsın və oradakı Reset() metodu yalnız bəzi ayarları sıfırlasın?

Belə hallarda qeyri-müəyyənlik yaranır və ya funksionallığı dəqiq ayırmaq istəyirsən. Bax, burada interfeysin açıq reallaşdırılması köməyə gəlir.

Interfeysin açıq reallaşdırılması sənə imkan verir ki, compiler-ə deyəsən: "Bu konkret metod məhz bu interfeys üçün nəzərdə tutulub və yalnız həmin interfeysə referans olanda əlçatandır". Bu, elə bil Peter Parker yalnız Spider-Man kimi çıxış edəndə tor atır, adi Peter Parker kimi isə fotoqraf bacarıqlarını göstərir.

2. Açıq reallaşdırma nə vaxt və niyə lazımdır

Sən interfeysi adi qaydada reallaşdıranda, onun metod və property-ləri class-ın public interfeysinin bir hissəsi olur. Amma bəzən istəyirsən ki, onlar yalnız interfeys vasitəsilə əlçatan olsun, birbaşa class-dan yox. Bu, düşündüyündən daha tez-tez olur! Məsələn, iki interfeys eyni adlı metod tələb edir (amma mənası fərqlidir), ya da sən istəyirsən ki, reallaşdırma yalnız interfeys vasitəsilə işləyənlər üçün açıq olsun.

Bax, burada açıq reallaşdırma köməyə gəlir. Bu, proqram arxitekturasında gizli keçid kimidir: çöldən görünmür, amma bilənlər — keçə bilər!

Ssenari 1: Ad konfliktləri

Təsəvvür elə, sənin bir class-ın var, iki interfeysi reallaşdırır, hər ikisi də eyni adlı, amma tam fərqli məntiqli metod tələb edir. Məsələn:

interface IWriter
{
    void Print();
}

interface IPrinter
{
    void Print();
}

Sən istəyirsən ki, IWriter.Print() mətn fayla yazsın, IPrinter.Print() isə printerə göndərsin. Sadə Print() metodu ilə bu davranışı ayıra bilməzsən. Burada açıq reallaşdırma vəziyyəti xilas edir.

Ssenari 2: Texniki/metodların gizlədilməsi

Bəzən class-ın interfeys metodunu reallaşdırmalıdır, amma istəmirsən ki, bu metod class-ı istifadə edən hər kəs üçün açıq olsun (məsələn, interfeys üzvü yalnız infrastruktur üçün lazımdır).

Ssenari 3: "Təsadüfi" çağırışdan qorunma

Əgər interfeys metodu birbaşa çağırış üçün nəzərdə tutulmayıbsa (məsələn, framework-un daxili mexanizmi üçün), onu açıq reallaşdırmaq olar — beləliklə, təsadüfi proqramçı onu class obyektindən birbaşa çağıra bilməz.

3. Açıq reallaşdırmanın sintaksisi

Əsas fərq — açıq reallaşdırmada metod və property-ləri interfeysin tam adı ilə yazırsan. Heç bir access modifier (public/private) və ya override açarı yoxdur!

Ümumi sintaksis:


Qaytarılan_tip InterfeysAdi.MetodAdi(parametrlər)
{
    // reallaşdırma
}
Açıq interfeys reallaşdırmasının sintaksisi

Bu, elə bil sən interfeysin adını açıq göstərirsən ki, compiler və komanda yoldaşların çaşmasın, bu reallaşdırma hansı müqaviləyə aiddir.

Ad konfliktinin həlli

Gəlin, IWriter və IPrinter ilə olan nümunəyə baxaq. Tutaq ki, bizdə report class-ı var:

interface IWriter
{
    void Print();
}

interface IPrinter
{
    void Print();
}

public class Report : IWriter, IPrinter
{
    // IWriter.Print-in açıq reallaşdırılması
    void IWriter.Print()
    {
        Console.WriteLine("Hesabatı fayla yazırıq (Writer)...");
    }

    // IPrinter.Print-in açıq reallaşdırılması
    void IPrinter.Print()
    {
        Console.WriteLine("Hesabatı kağız printerə göndəririk (Printer)...");
    }

    // İstifadəçi üçün əlavə metod
    public void Show()
    {
        Console.WriteLine("Hesabatı ekranda göstəririk.");
    }
}

İndi fərqli interfeyslərdən istifadə edək:

var report = new Report();

report.Show(); // Adi public metod

// report.Print(); // Səhv! Report class-ında belə metod yoxdur

IWriter writer = report;
writer.Print(); // IWriter.Print() reallaşdırmasını çağırır

IPrinter printer = report;
printer.Print(); // IPrinter.Print() reallaşdırmasını çağırır

Burada Print metodları birbaşa report obyektindən əlçatan deyil, amma lazımi interfeys vasitəsilə əlçatandır. Bu — açıq reallaşdırmanın əsas ideyasıdır: yalnız interfeys "portu" ilə həmin metoda çata bilərsən.

Bu yaddaşda necə görünür: sadə illüstrasiya

Əslində, açıq reallaşdırma interfeys üzvünü class-ın içində "gizlədir". Vizual olaraq belə bir cədvəl təsəvvür edə bilərsən:

Necə müraciət edirik Əslində nə çağırılır
report.Print()
Compiler səhvi — belə metod yoxdur
((IWriter)report).Print()
Açıq reallaşdırma
IWriter.Print()
((IPrinter)report).Print()
Açıq reallaşdırma
IPrinter.Print()
report.Show()
Class-ın Show metodu

4. Nümunə: İnterfeys — yalnız infrastruktur üçün

Real layihədə tez-tez belə olur: class hansısa texniki interfeysi reallaşdırmalıdır, amma bu reallaşdırma adi istifadəçilərə lazım deyil.

interface IBroadcastable
{
    void Broadcast();
}

public class SecretMessage : IBroadcastable
{
    void IBroadcastable.Broadcast()
    {
        Console.WriteLine("Gizli mesaj efirə getdi...");
    }

    public void Reveal()
    {
        Console.WriteLine("Siri ekranda göstəririk.");
    }
}

// Adi kodda:
var message = new SecretMessage();
message.Reveal();  // İstifadəçi metodu

// message.Broadcast(); // Səhv! — belə metod yoxdur

// Yalnız infrastruktur bilir nə etmək lazımdır:
((IBroadcastable)message).Broadcast();

Burada Broadcast() metodu — yalnız interfeys müqaviləsi ilə işləyənlər üçündür.

5. Property və indeksatorların açıq reallaşdırılması

Yalnız metodları yox, property və hətta indeksatorları da açıq reallaşdırmaq olar.

interface IDescribable
{
    string Description { get; }
}

public class Product : IDescribable
{
    // Property-nin açıq reallaşdırılması
    string IDescribable.Description => "Təsvir yalnız interfeys vasitəsilə əlçatandır";

    // Adi public property
    public string Name { get; set; }
}

// İstifadə nümunəsi:
var p = new Product { Name = "Qacet" };
// p.Description; // Səhv! Product-da belə property yoxdur

var descr = ((IDescribable)p).Description;
Console.WriteLine(descr);

6. Faydalı nüanslar

Açıq reallaşdırmada irsiyyət necə işləyir

İrsiyyətdə hər şey intuitivdir: əgər bazada interfeys açıq reallaşdırılıbsa, törəmə class bu "davranışı" miras alır. Amma əgər törəmə class interfeys metodunu yenidən reallaşdırmaq istəsə, bu mümkün deyil: açıq reallaşdırma virtual ola bilməz. Yəni, açıq reallaşdırılmış üzvü override etmək olmur.

Əgər belə davranış lazımdırsa — adi reallaşdırmadan istifadə et və ya "template method" pattern-ini nəzərdən keçir.

Açıq və gizli reallaşdırma


+----------------+
|   Invoice      |
+----------------+
| Show()         |   // Birbaşa çağırmaq olar
+----------------+
| ITxtExportable.Export()   // Yalnız ITxtExportable ilə
| IJsonExportable.Export()  // Yalnız IJsonExportable ilə
+----------------+

Açıq reallaşdırmanın xüsusiyyətləri/məhdudiyyətləri

  • Açıq reallaşdırılmış metod və property-lər access modifier-lərə sahib ola bilməz. Onlar default olaraq xaricə bağlıdır və yalnız interfeys vasitəsilə əlçatandır.
  • Belə üzvləri static, virtual, abstract və ya override etmək olmaz.
  • Açıq reallaşdırılmış üzvə class obyektindən birbaşa müraciət etmək olmaz (yalnız interfeys tipli dəyişənlə).
  • Əgər interfeys başqa interfeysdən miras alırsa, istənilən səviyyənin üzvlərini açıq reallaşdırmaq olar.

7. Açıq reallaşdırmanın üstünlükləri

Açıq reallaşdırma — sadəcə ad konfliktlərini həll etmək üçün sintaksis fəndi deyil. Onun bir neçə ciddi səbəbi var:

Ad konfliktlərinin həlli (The Big One): Ən əsas və aydın səbəb budur. Əgər iki interfeys, hansı ki, sən reallaşdırırsan, eyni imzalı metod (və ya property) elan edirsə, açıq reallaşdırma hər interfeys üçün ayrıca, spesifik reallaşdırma verməyə imkan verir. Onsuz qeyri-müəyyənlik yaranardı.
Real həyatdan nümunə: Təsəvvür elə, sənin printerin var. O, eyni zamanda skaner də ola bilər. IPrinter interfeysində Print() metodu var, IScanner interfeysində isə Scan() metodu. Amma əgər hər iki interfeysdə ProcessDocument() metodu olsaydı? Açıq reallaşdırma sənə imkan verir ki, IPrinter.ProcessDocument() çap üçün, IScanner.ProcessDocument() isə scan üçün ayrıca işləsin.

Reallaşdırma detallarının gizlədilməsi və class API-nin təmizliyi: Açıq reallaşdırılmış metodlar class-ın public API-sinin bir hissəsi deyil. Onlar yalnız obyekt interfeys tipinə cast olunanda əlçatandır. Bu çox faydalıdır, əgər istəyirsən ki, müəyyən funksiya yalnız "müqavilə" ilə əlçatan olsun, class-ın ümumi davranışının bir hissəsi kimi yox.
Nümunə: Sən mürəkkəb maliyyə aləti yaradırsan, məsələn, CreditCard. O, IPayable (ödəmələr üçün) və IAdminConfigurable (daxili ayarlar üçün, məsələn, limit qoymaq) interfeyslərini reallaşdırır. IAdminConfigurable.SetLimit() metodu hər kəs üçün açıq olmamalıdır, yalnız CreditCard-a IAdminConfigurable kimi baxan admin sistemi üçün açıq olmalıdır. Açıq reallaşdırma SetLimit()-i ümumi CreditCard üçün gizli saxlayır, class API-sini daha təmiz və təhlükəsiz edir.

Müqavilə zəmanəti: Bəzən sənin class-ında təsadüfən interfeysdəki metodla eyni imzalı metod olur, amma istəmirsən ki, o, interfeysin reallaşdırması sayılsın. Məsələn, sənin MyList class-ında Clear() metodu var, hansı ki, daxili vəziyyəti təmizləyir. Əgər sən MyList-i IList<T> kimi reallaşdırmaq istəsən, orada da Clear() var, default olaraq sənin MyList.Clear() metodun IList<T>.Clear() reallaşdırması olacaq. Əgər onların məntiqi fərqlidirsə, açıq IList<T>.Clear() reallaşdırması ilə ayıra bilərsən.

Gizli (Implicit) vs. Açıq (Explicit) reallaşdırma

Fərqi aydın görmək üçün kiçik müqayisə cədvəlinə baxaq:

Xüsusiyyət Gizli (Implicit) reallaşdırma Açıq (Explicit) reallaşdırma
Əlçatanlıq Həm class, həm də interfeys vasitəsilə əlçatandır. Yalnız interfeys vasitəsilə əlçatandır.
Access modifier Adətən public (və ya protected və s.). Access modifier yoxdur (public ola bilməz).
Sintaksis
public ReturnType MethodName(Params) { ... }
ReturnType InterfaceName.MethodName(Params) { ... }
Konfliktlərin həlli Həll etmir, metod bütün interfeyslər üçün işləyir. Konfliktləri həll edir, fərqli reallaşdırma verir.
Class API-nin təmizliyi Metod class-ın public API-sinin bir hissəsidir. Metod class-ın public API-sinin bir hissəsi deyil.
Təyinat Ümumi, birmənalı interfeys reallaşdırmaları üçün. Konfliktlərin həlli və ya spesifik məntiqin gizlədilməsi üçün.

Gördüyün kimi, bunlar bir medalın iki üzüdür və hərəsinin öz yeri var. Əksər hallarda gizli reallaşdırmadan istifadə edəcəksən, çünki o, daha sadə və rahatdır. Açıq reallaşdırma isə spesifik, amma vacib hallarda istifadə olunur.

8. Açıq interfeys reallaşdırmasında tipik səhvlər

Səhv №1: metod class obyektindən görünmür.
Açıq reallaşdırılmış metodlar class nümunəsi ilə birbaşa əlçatan deyil. Tez-tez qarışıqlıq yaranır: compiler deyir ki, metod tapılmadı, halbuki var — sadəcə "gizlənib". Həll: obyekti interfeys tipinə cast et, məsələn: (IMyInterface)obj.Method().

Səhv №2: access modifier-lərin səhv istifadəsi.
Açıq reallaşdırmada public və ya override kimi modifier-lər yazmaq olmaz. Bunu etsən, compiler səhv verəcək — belə elanlar qəbul olunmur.

Səhv №3: açıq reallaşdırılmış metodu törəmə class-da override etmək cəhdi.
Əgər interfeys metodu bazada açıq reallaşdırılıbsa, onu törəmə class-da override etmək olmur. Bu məhdudiyyəti interfeysli class iyerarxiyası dizayn edəndə nəzərə al.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Bir interfeysin açıq şəkildə reallaşdırılması ilə sinif yaradılması
Bir interfeysin açıq şəkildə reallaşdırılması ilə sinif yaradılması
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
İki interfeysin reallaşdırılmasında adların toqquşmasının həlli
İki interfeysin reallaşdırılmasında adların toqquşmasının həlli
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
İnterfeys anlayışı
İnterfeyslər: əsaslar və kontraktlar
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION