MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Definirea operatorilor

Alături de metodele din clase și structuri, putem defini și operatori. De exemplu, să presupunem că avem următoarea clasă Counter:

class Counter
{
   public int Value { get; set; }
}

Această clasă reprezintă un contor, a cărui valoare este stocată în proprietatea Value.

Și să presupunem că avem două obiecte ale clasei Counter - două contoare pe care vrem să le comparăm sau să le adunăm pe baza proprietății lor Value, folosind operațiile standard de comparare și adunare:

Counter counter1 = new Counter { Value = 23 };
Counter counter2 = new Counter { Value = 45 };

bool result = counter1 > counter2;
Counter c3 = counter1 + counter2;

Dar în acest moment nici operația de comparare, nici cea de adunare pentru obiectele Counter nu sunt disponibile. Aceste operații pot fi folosite pentru o serie de tipuri primitive.

De exemplu, în mod implicit putem aduna valori numerice, dar cum să adunăm obiecte de tip complex - clase și structuri, compilatorul nu știe. Și pentru asta trebuie să facem supraîncărcarea operatorilor de care avem nevoie.

Definirea operatorilor constă în definirea în clasă, pentru obiectele căreia vrem să definim operatorul, a unei metode speciale:

public static return_type operator operator(parameters)
{ }

Această metodă trebuie să aibă modificatorii public static, deoarece operatorul supraîncărcat va fi folosit pentru toate obiectele clasei respective. Apoi urmează tipul returnat. Tipul returnat reprezintă tipul obiectelor pe care dorim să le obținem.

De exemplu, în urma adunării a două obiecte Counter, așteptăm să obținem un nou obiect Counter. Iar în urma comparării a două obiecte dorim să obținem un obiect de tip bool, care indică dacă expresia condițională este adevărată sau falsă. Dar, în funcție de sarcină, tipurile returnate pot fi oricare.

Apoi, în locul numelui metodei, apare cuvântul cheie operator și propriul operator. Și mai departe în paranteze sunt enumerați parametrii. Operatorii binari acceptă doi parametri, iar cei unari - un parametru. Și în orice caz, unul dintre parametri trebuie să reprezinte tipul - clasa sau structura în care este definit operatorul.

De exemplu, să supraîncărcăm o serie de operatori pentru clasa Counter:

class Counter
{
   public int Value { get; set; }
       
   public static Counter operator +(Counter counter1, Counter counter2)
   {
       return new Counter { Value = counter1.Value + counter2.Value };
   }
   public static bool operator >(Counter counter1, Counter counter2)
   {
       return counter1.Value > counter2.Value;
   }
   public static bool operator <(Counter counter1, Counter counter2)
   {
       return counter1.Value < counter2.Value;
   }
}

Deoarece toți operatorii definiți sunt binari - adică se efectuează asupra a două obiecte, pentru fiecare supraîncărcare sunt prevăzuți doi parametri.

Pentru că în cazul operației de adunare vrem să adunăm două obiecte ale clasei Counter, operatorul acceptă două obiecte ale acestei clase. Și pentru că vrem ca în urma adunării să obținem un nou obiect Counter, această clasă este folosită și ca tip returnat.

Toate acțiunile acestui operator se reduc la crearea unui nou obiect, a cărui proprietate Value combină valorile proprietății Value ale ambilor parametri:

public static Counter operator +(Counter counter1, Counter counter2)
{
   return new Counter { Value = counter1.Value + counter2.Value };
}

De asemenea, sunt definite două operații de comparare. Dacă definim una dintre aceste operații de comparare, trebuie să o definim și pe a doua. Operatorii de comparare compară valorile proprietăților Value și, în funcție de rezultatul comparației, returnează fie true, fie false.

Acum folosim operatorii supraîncărcați în program:

Counter counter1 = new Counter { Value = 23 };
Counter counter2 = new Counter { Value = 45 };
bool result = counter1 > counter2;
Console.WriteLine(result); // false

Counter counter3 = counter1 + counter2;
Console.WriteLine(counter3.Value);  // 23 + 45 = 68

Este de remarcat faptul că, deoarece definirea operatorului reprezintă de fapt o metodă, putem supraîncărca această metodă, adică să creăm o altă versiune pentru ea. De exemplu, adăugăm în clasa Counter un alt operator:

public static int operator +(Counter counter1, int val)
{
   return counter1.Value + val;
}

Această metodă adună valoarea proprietății Value și un anumit număr, returnând suma lor. Și de asemenea putem aplica acest operator:

Counter counter1 = new Counter { Value = 23 };
int result = counter1 + 27; // 50
Console.WriteLine(result);

Trebuie de avut în vedere că nu toți operatorii pot fi definiți. În special, putem defini logica pentru următorii operatori:

  • operatori unari: +x, -x, !x, ~x, ++, --, true, false
  • operatori binari: +, -, *, /, %
  • operații de comparare: ==, !=, <, >, <=, >=
  • operatori pe biți: &, |, ^, <<, >>
  • operatori logici: &&, ||

În plus, există câțiva operatori care trebuie definiți în perechi:

  • == și !=
  • < și >
  • <= și >=

Și există o serie de operatori care nu pot fi supraîncărcați, de exemplu, operația de egalitate = sau operatorul ternar ?:, precum și alții. Lista completă a operatorilor supraîncărcabili poate fi găsită în documentația MSDN.

Definirea incrementului și decrementului

Trebuie de avut în vedere că în codul operatorului nu trebuie să se modifice obiectele care sunt transmise în operator prin parametri. De exemplu, putem defini pentru clasa Counter operatorul de incrementare:

public static Counter operator ++(Counter counter1)
{
   counter1.Value += 10;
   return counter1;
}

Deoarece operatorul este unar, acceptă un singur parametru - obiectul clasei în care este definit operatorul. Dar aceasta este o definiție incorectă a incrementării, deoarece operatorul nu trebuie să modifice valorile parametrilor săi.

O definiție mai corectă a operatorului de incrementare ar arăta astfel:

public static Counter operator ++(Counter counter1)
{
   return new Counter { Value = counter1.Value + 10 };
}

Adică se returnează un nou obiect, care conține în proprietatea Value valoarea incrementată. Nu trebuie să definim separat operatorii pentru incrementarea prefixată și postfixată (și nici pentru decrementare), deoarece o singură implementare va funcționa în ambele cazuri.

Counter counter1 = new Counter() { Value = 10 };
Counter counter2 = counter1++;
Console.WriteLine(counter1.Value);      // 20
Console.WriteLine(counter2.Value);      // 10

Counter counter3 = ++counter1;
Console.WriteLine(counter1.Value);      // 30
Console.WriteLine(counter3.Value);      // 30

În cazul operației de incrementare postfixată (counter1++), compilatorul creează mai întâi o variabilă temporară în care salvează obiectul curent. Apoi, obiectul curent este înlocuit cu valoarea obținută din funcția operatorului.

Ca rezultat al operației, se returnează valoarea variabilei temporare. În cazul incrementării prefixate (++counter1), compilatorul returnează noua valoare obținută din funcția operatorului.

Definirea operațiilor true și false

Separat, merită menționată definirea operatorilor true și false. Acești operatori sunt definiți atunci când dorim să folosim un obiect de tip ca și condiție. De exemplu, să definim acești operatori în clasa Counter:

class Counter
{
   public int Value { get; set; }
   
   public static bool operator true(Counter counter1)
   {
       return counter1.Value != 0;
   }
   public static bool operator false(Counter counter1)
   {
       return counter1.Value == 0;
   }
}

De exemplu:

Counter counter = new Counter() { Value = 0 };
if (counter)
   Console.WriteLine(true);
else
   Console.WriteLine(false);

De asemenea, este de remarcat faptul că dacă dorim să folosim operația de negație, tipul if (!counter), trebuie să definim și operația ! pentru tipul respectiv:

Counter counter = new Counter() { Value = 2 };
if (!counter)
   Console.WriteLine(true);
else
   Console.WriteLine(false);

class Counter
{
   public int Value { get; set; }

   public static bool operator !(Counter counter1)
   {
       return counter1.Value == 0;
   }

   public static bool operator true(Counter counter1)
   {
       return counter1.Value != 0;
   }
   public static bool operator false(Counter counter1)
   {
       return counter1.Value == 0;
   }
}

Operația de negație este de fapt sinonimă cu operația false, de aceea conține o condiție similară.

← Lecția anterioară Lecția următoare →