MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Aplicarea interfețelor

Înțelegem cum funcționează rețelele neuronale și creăm una pentru căutarea știrilor.

O interfață reprezintă o descriere a unui tip, un set de componente pe care trebuie să le aibă un tip de date. Și, propriu-zis, nu putem crea obiecte de tip interfață direct cu ajutorul unui constructor, așa cum facem în cazul claselor:

IMovable m = new IMovable(); // ! Eroare, acest lucru nu este permis

interface IMovable
{
   void Move();
}

În cele din urmă, interfața este destinată implementării în clase și structuri. De exemplu, să implementăm interfața IMovable definită mai sus:

// aplicarea interfeței într-o clasă
class Person : IMovable
{
   public void Move()
   {
       Console.WriteLine("Omul merge");
   }
}

// aplicarea interfeței într-o structură
struct Car : IMovable
{
   public void Move()
   {
       Console.WriteLine("Mașina se deplasează");
   }
}

La aplicarea interfeței, la fel ca la moștenire, după numele clasei sau structurii se indică două puncte și apoi urmează denumirile interfețelor aplicate. În acest caz, clasa trebuie să implementeze toate metodele și proprietățile interfețelor aplicate, dacă aceste metode și proprietăți nu au implementare implicită.

Dacă metodele și proprietățile interfeței nu au modificator de acces, implicit ele sunt publice; la implementarea acestor metode și proprietăți în clasă și structură, putem folosi doar modificatorul public.

Aplicarea interfeței într-un program:

Person person = new Person();
Car car = new Car();
DoAction(person);
DoAction(car);

void DoAction(IMovable movable) => movable.Move();

interface IMovable
{
   void Move();
}

class Person : IMovable
{
   public void Move() => Console.WriteLine("Omul merge");
}

struct Car : IMovable
{
   public void Move() => Console.WriteLine("Mașina se deplasează");
}

În acest program este definită metoda DoAction(), care acceptă ca parametru un obiect de tip interfață IMovable. La momentul scrierii codului, este posibil să nu știm ce va fi acest obiect - o clasă sau o structură anume. Singurul lucru de care putem fi siguri este că acest obiect va implementa neapărat metoda Move, iar noi putem apela această metodă.

Cu alte cuvinte, interfața este un contract care specifică faptul că un anumit tip va implementa o anumită funcționalitate.

Ieșirea în consolă a programului:

Omul merge
Mașina se deplasează

Implementarea implicită a interfețelor

Începând cu versiunea C# 8.0, interfețele suportă implementarea implicită a metodelor și proprietăților. De ce este necesar acest lucru? Să presupunem că avem multe clase care implementează o anumită interfață. Dacă adăugăm o nouă metodă în această interfață, vom fi obligați să implementăm această metodă în toate clasele care aplică interfața respectivă.

În caz contrar, aceste clase nu vor fi compilate. Acum, în loc să implementăm metoda în toate clasele, putem defini implementarea sa implicită în interfață. Dacă o clasă nu implementează metoda, se va aplica implementarea implicită.

IMovable tom = new Person();
Car tesla = new Car();
tom.Move();     // Walking
tesla.Move();   // Driving

interface IMovable
{
   void Move() => Console.WriteLine("Walking");
}

class Person : IMovable { }

class Car : IMovable
{
   public void Move() => Console.WriteLine("Driving");
}

În acest caz, interfața IMovable definește implementarea implicită pentru metoda Move. Clasa Person nu implementează această metodă, așa că folosește implementarea implicită, spre deosebire de clasa Car, care definește propria sa implementare pentru metoda Move.

Este de remarcat faptul că, deși pentru obiectul clasei Person putem apela metoda Move - deoarece clasa Person aplică interfața IMovable, nu putem scrie astfel:

Person tom = new Person();
tom.Move();     // Eroare - metoda Move nu este definită în clasa Person

Implementarea multiplă a interfețelor

Interfețele au încă o funcție importantă: în C# nu se suportă moștenirea multiplă, adică putem moșteni o clasă doar dintr-o singură clasă, spre deosebire de limbajul C++, unde moștenirea multiplă poate fi folosită.

Interfețele permit să ocolim parțial această limitare, deoarece în C# clasele și structurile pot implementa mai multe interfețe simultan. Toate interfețele implementate sunt indicate prin virgulă:

class MyClass : IMyInterface1, IMyInterface2, IMyInterface3
{
   // Implementare interfețe
}

Să analizăm un exemplu:

Message hello = new Message("Hello World");
hello.Print();  // Hello World

interface IMessage
{
   string Text { get; set; }
}

interface IPrintable
{
   void Print();
}

class Message : IMessage, IPrintable
{
   public string Text { get; set; }
   public Message(string text) => Text = text;
   public void Print() => Console.WriteLine(Text);
}

În acest caz, sunt definite două interfețe. Interfața IMessage definește proprietatea Text, care reprezintă textul mesajului. Iar interfața IPrintable definește metoda Print.

Clasa Message implementează ambele interfețe și apoi este utilizată în program.

Interfețe în conversiile de tipuri

Tot ce s-a spus despre conversiile de tipuri este valabil și pentru interfețe. Deoarece clasa Message implementează interfața IMessage, variabila de tip IMessage poate stoca o referință la un obiect de tip Message:

// Toate obiectele Message sunt obiecte IMessage
IMessage hello = new Message("Hello METANIT.COM");
Console.WriteLine(hello.Text); // Hello METANIT.COM

// Nu toate obiectele IMessage sunt obiecte Message, este necesară conversia explicită
// Message someMessage = hello; // ! Eroare

// Interfața IMessage nu are metoda Print, este necesară conversia explicită
// hello.Print();  // ! Eroare

// Dacă hello reprezintă clasa Message, realizăm conversia
if (hello is Message someMessage) someMessage.Print();

Conversia de la o clasă la interfața sa, la fel ca și conversia de la un tip derivat la un tip de bază, se realizează automat. Deoarece orice obiect Message implementează interfața IMessage.

Conversia inversă - de la o interfață la clasa care o implementează - va fi similară conversiei de la o clasă de bază la una derivată. Deoarece nu orice obiect IMessage este un obiect Message (deoarece interfața IMessage poate fi implementată și de alte clase), pentru această conversie este necesară o operație de casting.

Și dacă dorim să accesăm metodele clasei Message, care nu sunt definite în interfața IMessage, dar fac parte din clasa Message, trebuie să realizăm explicit conversia tipurilor:

if (hello is Message someMessage) someMessage.Print();