MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Clase abstracte și membrii claselor

Clase abstracte

În C#, pe lângă clasele obișnuite, există și clase abstracte. Ce rol au acestea? Clasele obișnuite reprezintă de obicei un plan pentru anumite tipuri de obiecte sau entități. De exemplu, putem defini clasa Car pentru a reprezenta mașinile sau clasa Person pentru a reprezenta persoanele, incluzând în aceste clase proprietăți, câmpuri și metode corespunzătoare care descriu aceste obiecte.

Cu toate acestea, unele entități pe care dorim să le exprimăm prin intermediul limbajului de programare nu au o manifestare concretă. De exemplu, în realitate nu există o formă geometrică ca atare.

Există cercuri, dreptunghiuri, pătrate, dar nu există pur și simplu o "formă". Totuși, atât cercul cât și dreptunghiul au ceva în comun și sunt forme. Pentru a descrie astfel de entități care nu au o manifestare concretă sunt destinate clasele abstracte.

O clasă abstractă este similară cu o clasă obișnuită. Aceasta poate avea variabile, metode, constructori, proprietăți. Singura diferență este că la definirea claselor abstracte se folosește cuvântul cheie abstract. De exemplu, să definim o clasă abstractă care reprezintă un vehicul:

abstract class Transport
{
   public void Move()
   {
       Console.WriteLine("Vehiculul se deplasează");
   }
}

Un vehicul reprezintă o abstracție care nu are o manifestare concretă. Adică, există mașini și camioane, avioane, nave maritime, unii se plimbă chiar și pe nave spațiale, dar ca un vehicul concret nu există.

Cu toate acestea, toate vehiculele au ceva comun - ele se pot deplasa. Pentru aceasta, în clasă este definită metoda Move, care simulează deplasarea.

Dar principalul aspect care diferențiază clasele abstracte de cele obișnuite este că nu putem folosi constructorul unei clase abstracte pentru a crea o instanță a clasei. De exemplu, nu putem face asta în felul următor:

Transport tesla = new Transport(); // Eroare! Nu se poate instantia o clasa abstracta

Clasele abstracte sunt folositoare pentru a descrie funcționalități comune care pot fi moștenite și utilizate de clasele derivate:

Transport car = new Car();
Transport ship = new Ship();
Transport aircraft = new Aircraft();

car.Move();
ship.Move();
aircraft.Move();

abstract class Transport
{
   public void Move()
   {
       Console.WriteLine("Vehiculul se deplasează");
   }
}

// clasă de tip navă
class Ship : Transport { }

// clasă de tip avion
class Aircraft : Transport { }

// clasă de tip automobil
class Car : Transport { }

În acest caz, de la clasa Transport sunt moștenite trei clase care reprezintă diferite tipuri de vehicule. Cu toate acestea, ele au o trăsătură comună - ele se pot deplasa folosind metoda Move().

Deși nu putem folosi constructorul unei clase abstracte pentru a crea instanțe ale acesteia, clasa abstractă poate defini constructori:

Transport car = new Car("automobil");
Transport ship = new Ship("navă");
Transport aircraft = new Aircraft("avion");

car.Move();         // automobilul se deplasează
ship.Move();        // nava se deplasează
aircraft.Move();    // avionul se deplasează

abstract class Transport
{
   public string Name { get; private set; }
   // constructorul clasei abstracte Transport
   public Transport(string name)
   {
       Name = name;
   }
   public void Move() => Console.WriteLine($"{Name} se deplasează");
}

// clasă de tip navă
class Ship : Transport 
{
   // apelăm constructorul clasei de bază
   public Ship(string name) : base(name) { }
}

// clasă de tip avion
class Aircraft : Transport
{
   public Aircraft(string name) : base(name) { }
}

// clasă de tip automobil
class Car : Transport
{
   public Car(string name) : base(name) { }
}

În acest caz, în clasa abstractă Transport este definit un constructor care, printr-un parametru, stabilește valoarea proprietății Name, care păstrează numele vehiculului. Astfel, clasele derivate trebuie să apeleze acest constructor în constructorii proprii.

Membri abstracti ai claselor

O clasă abstractă poate avea membri abstracti, care sunt definiți cu cuvântul cheie abstract și nu au implementare. Acești membri pot fi:

  • Metode
  • Proprietăți
  • Indexatori
  • Evenimente

Membrii abstracti ai claselor nu pot avea modificatorul private. O clasă derivată trebuie să implementeze toți membrii abstracti ai clasei de bază. Când un membru abstract este suprascris într-o clasă derivată, acesta trebuie declarat cu modificatorul override.

Dacă o clasă conține cel puțin un membru abstract, atunci acea clasă trebuie să fie declarată ca fiind abstractă.

Metode abstracte

De exemplu, în cazul vehiculelor, metoda Move ar putea descrie modul în care un vehicul se deplasează. Totuși, diferite tipuri de vehicule se deplasează în moduri diferite - unele pe pământ, altele prin aer, altele pe apă. În acest caz, am putea face metoda Move abstractă, iar implementarea ei să fie responsabilitatea claselor derivate:

abstract class Transport
{
   public abstract void Move();
}

class Car : Transport
{
   public override void Move()
   {
       Console.WriteLine("Mașina merge");
   }
}

class Ship : Transport
{
   public override void Move()
   {
       Console.WriteLine("Nava navighează");
   }
}

Aplicarea claselor:

Transport car = new Car();
Transport ship = new Ship();
Transport aircraft = new Aircraft();

car.Move();         // mașina merge
ship.Move();        // corabia navighează
aircraft.Move();    // avionul zboară

Proprietăți abstracte

Este important să subliniem utilizarea proprietăților abstracte. Definirea acestora este similară cu definirea proprietăților automate. De exemplu:

abstract class Transport
{
   // proprietate abstractă pentru stocarea vitezei
   public abstract int Speed { get; set; }
}
// clasă de tip navă
class Ship: Transport
{
   int speed;
   public override int Speed
   {
       get => speed;
       set => speed = value;
   }
}

class Aircraft : Transport
{
   public override int Speed { get; set; }
}

În clasa Transport este definită o proprietate abstractă Speed, care trebuie să stocheze viteza vehiculului. Aceasta seamănă cu o proprietate auto, dar nu este o proprietate auto. Deoarece această proprietate nu trebuie să aibă o implementare, conține doar blocuri goale pentru get și set.

În clasele derivate, putem suprascrie această proprietate, transformând-o într-o proprietate completă (cum este în clasa Ship), sau o putem menține ca o proprietate auto (cum este în clasa Aircraft).

Renunțarea la implementarea membrilor abstracti

Clasa derivată este obligată să implementeze toți membrii abstracti ai clasei de bază. Totuși, putem renunța la implementare, dar în acest caz clasa derivată trebuie să fie, de asemenea, definită ca fiind abstractă:

ransport tesla = new Auto();
tesla.Move();           // mașina se deplasează
abstract class Transport
{
   public abstract void Move();
}
// clasă de tip automobil
abstract class Car : Transport{}

class Auto: Car
{
   public override void Move()
   {
       Console.WriteLine("mașina se deplasează");
   }
}

În acest caz, clasa Car nu implementează metoda abstractă Move a clasei de bază Transport și, prin urmare, este definită ca fiind abstractă. Totuși, orice clasă non-abstractă derivată din Car trebuie să implementeze toți membrii abstracti moșteniți.

Exemplu de clasă abstractă

Un exemplu clasic este sistemul de forme geometrice. În realitate, nu există o formă geometrică ca atare. Există cercuri, dreptunghiuri, pătrate, dar nu există pur și simplu o "formă". Totuși, atât cercul cât și dreptunghiul au ceva în comun și sunt forme:

// clasă abstractă pentru forme
abstract class Shape
{
   // metodă abstractă pentru obținerea perimetrului
   public abstract double GetPerimeter();
   // metodă abstractă pentru obținerea ariei
   public abstract double GetArea();
}
// clasă derivată pentru dreptunghiuri
class Rectangle : Shape
{
   public float Width { get; set; }
   public float Height { get; set; }

   // suprascrierea obținerii perimetrului
   public override double GetPerimeter() => Width * 2 + Height * 2;
   // suprascrierea obținerii ariei
   public override double GetArea() => Width * Height;
}
// clasă derivată pentru cercuri
class Circle : Shape
{
   public double Radius { get; set; }

   // suprascrierea obținerii perimetrului
   public override double GetPerimeter() => Radius * 2 * 3.14;
   // suprascrierea obținerii ariei
   public override double GetArea() => Radius * Radius * 3.14;
}

Utilizarea claselor:

var rectangle = new Rectangle { Width = 20, Height = 20 };
var circle = new Circle { Radius = 200 };
PrintShape(rectangle); // Perimeter: 80   Area: 400
PrintShape(circle); // Perimeter: 1256  Area: 125600

void PrintShape(Shape shape)
{
   Console.WriteLine($"Perimeter: {shape.GetPerimeter()}  Area: {shape.GetArea()}");
}
← Lecția anterioară Lecția următoare →