Przeciążanie operatorów w języku C#
Przeciążanie operatorów - definiowanie operatorów dla własnych typów.
Język
C# pozwala zdefiniować funkcjonalność operatorów dla własnych typów, czyli np. stworzonych przez nas obiektów. Dzięki takim zabiegom zamiast pisać i wywoływać za każdym razem metodę odejmowania możemy nadpisać operator, czyli utworzyć metodę, która coś zwraca, przyjmuje parametry oraz posiada w deklaracji słowo kluczowe "
operator" wraz z symbolem operatora, który nadpisujemy a więc w naszym wypadku będzie to "
operator-". Każdy operator ma określoną liczbę argumentów, dla operatora "-" musza to być 2 argumenty.
Składnie definicji przeciążonego operatora możemy podzielić na 4 typy:
-
public static zwracany_typ operator op_unarny(typ_arg argument),
-
public static zwracany_typ operator op_binarny(typ_arg1 argument1, typ_arg2 argument2),
-
public static implicit operator typ_wyj(typ_wej argument),
-
public static explicit operator typ_wej(typ_wyj argument)
gdzie:
- zwracany_typ - typ zwracany przez operator (wymagane),
- op_unarny - operator unarny dostępny do przeciążania: +, -, !, ~, ++, --, true, false (wymagane),
- op_binarny - operator binarny dostępny do przeciążania: +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^ (wymagane),
- typ_arg - typy argumentów wejściowych (wymagane),
- argument - nazwa argumentu wejściowego (wymagane),
- typ_wyj - typ wyjściowy operatora konwersji (wymagane),
- typ_wej - typ wejściowy operatora konwersji (wymagane).
Operatory których nie wolno przeciążać:
- logiczne warunkowe: &&, ||,
- konwersji: () - służą do tego słowa kluczowe explicit i implicit,
- indeksacji: [] - stosujemy w tym celu indeksatory,
- przypisań: +=, -=, *=, %=, <<=, >>=, |=, ^=, &=, /=,
- pozostałe: =, ., new, is, sizeof, typeof, ?:.
Przeciążanie operatorów relacji
Operatory relacji czyli operatory takie jak: "==" i "!=", "<" i ">", "<=" i ">=" muszą być
przeciążane parami, nie można przeciążyć tylko jednego z pary operatorów.
Equals() i GetHashCode()
Przeciążając operatory "==" i "!=" powinniśmy również nadpisać metody
Equals() i
GetHashCode(), które są dziedziczone z klasy
System.Object. Metoda
Equals() działa identycznie jak operator "==" natomiast metoda
GetHashCode() jest to wartość liczbowa, służąca do identyfikacji obiektu podczas testowania równości. Jeśli wynik metody
Equals() daje wartość true to metoda
GetHashCode() musi zwracać te same wartości dla obydwu obiektów, jeśli natomiast metoda
Equals() zwraca wartość false to metoda
GetHashCode() może zwracać takie same lub różne wartości dla porównywanych obiektów.
Metoda
GetHashCode() musi zwracać te samą wartość tak długo, jak długo zmiany obiektu nie wpływają na zmianę wyniku metody
Equals().
Przeciążanie operatorów konwersji
Można przeciążać operator konwersji w postaci jawnej (
explicit) oraz w postaci niejawnej (
implicit). Aby przeciążyć
operator niejawny używamy słowa kluczowego
implicit, natomiast aby przeciążyć
operator jawny, używamy słowa kluczowego
explicit w definicji operatora.
Przykładowy kod - konwersja jawna:
class Obwod
{
public int _bok1;
public int _bok2;
public double _bok3;
public Obwod(int bok1, int bok2, double bok3)
{
_bok1 = bok1;
_bok2 = bok2;
_bok3 = bok3;
}
public static explicit operator double(Obwod f)
{
return f._bok1 * f._bok2 * f._bok3;
}
}
class Wyswietl
{
public static void Main()
{
Obwod fig1 = new Obwod(3, 4, 4.34);
Obwod fig2 = new Obwod(4, 5, 4.34);
Obwod fig3 = new Obwod(5, 6, 4.34);
double obwod = (double)fig1;
Console.WriteLine("Obwód wynosi: " + obwod);
}
}
Przykładowy kod - konwersja niejawna:
class Obwod
{
public int _bok1;
public int _bok2;
public double _bok3;
public Obwod(int bok1, int bok2, double bok3)
{
_bok1 = bok1;
_bok2 = bok2;
_bok3 = bok3;
}
public static implicit operator double(Obwod f)
{
return f._bok1 * f._bok2 * f._bok3;
}
}
class Wyswietl
{
public static void Main()
{
Obwod fig1 = new Obwod(3, 4, 4.34);
Obwod fig2 = new Obwod(4, 5, 4.34);
Obwod fig3 = new Obwod(5, 6, 4.34);
double obwod = fig1;
Console.WriteLine("Obwód wynosi: " + obwod);
}
}
Jak łatwo zauważyć jedyna różnica pomiędzy tymi dwoma rozwiązaniami sprowadza się do tego, że przy korzystaniu z konwersji jawnej musimy wykonać rzutowanie na typ
double.
Przeciążanie operatorów logicznych
Operatory logiczne są rozwijane za pomocą:
&, |, true, false dlatego nie ma możliwości rozwinięcia operatorów logicznych wprost. Aby rozwinąć wyrażenie z operatorem
&& lub
|| musimy przeciążyć operatory
&, |, true oraz
false.
Rozwinięcie wygląda następująco:
- dla operatora: && jest to T.false(x) ? x : T.&(x,y),
- dla operatora: || jest to T.true(x) ? x : T.|(x,y).
gdzie:
T jest to typ zmiennych x i y.
Przykładowy kod:
class Obwod
{
public int _bok1;
public int _bok2;
public Obwod(int bok1, int bok2)
{
_bok1 = bok1;
_bok2 = bok2;
}
public static Obwod operator &(Obwod ob1, Obwod ob2)
{
return new Obwod(ob1._bok1 & ob2._bok1, ob1._bok2 & ob2._bok2 );
}
public static Obwod operator |(Obwod ob1, Obwod ob2)
{
return new Obwod(ob1._bok1 | ob2._bok1, ob1._bok2 | ob2._bok2);
}
public static bool operator true(Obwod ob1)
{
return ob1._bok1 != 0;
}
public static bool operator false(Obwod ob1)
{
return ob1._bok1 == 0;
}
}
class Wyświetl
{
public static void Main()
{
Obwod fig1 = new Obwod(3, 4);
Obwod fig2 = new Obwod(4, 5);
Obwod fig3 = new Obwod(5, 6);
if(fig1 && fig2)
Console.WriteLine("Długości boków różne od zera");
if (fig1 || fig3)
Console.WriteLine("Długości boków fig1 lub fig2 różne od zera");
}
}
Dzięki przeciążeniu operatorów możemy użyć operatora
&& lub
|| dla obiektu typu
Obwod. Bez przeciążenia nie było by możliwości zastosowania tych operatorów do działań na obiektach z klasy Obwod.
Przeciążanie operatorów arytmetycznych
W języku C# możemy przeciążać następujące
operatory arytmetyczne:
+, -, /, *. Jedynym ograniczeniem, jakie występuje w przypadku przeciążania operatorów arytmetycznych jest to, że
przynajmniej jeden argument definiowanego operatora musi być obiektem klasy, dla której został przeciążony.
Przykładowy kod:
class Obwod
{
public int _bok1;
public int _bok2;
public double _bok3;
public Obwod(int bok1, int bok2, double bok3)
{
_bok1 = bok1;
_bok2 = bok2;
_bok3 = bok3;
}
public static Obwod operator +(Obwod ob1, Obwod ob2)
{
return new Obwod(ob1._bok1 + ob2._bok1, ob1._bok2 + ob2._bok2, ob1._bok3 + ob2._bok3);
}
public static Obwod operator -(Obwod ob1, int liczba)
{
return new Obwod(ob1._bok1 - liczba, ob1._bok2 - liczba, ob1._bok3 - liczba);
}
}
class Wyswietl
{
public static void Main()
{
Obwod fig1 = new Obwod(3, 4, 4.34);
Obwod fig2 = new Obwod(4, 5, 4.34);
Obwod fig3 = new Obwod(5, 6, 4.34);
Obwod obwod = fig1 + fig2 - 2;
Console.WriteLine("Obwód wynosi: " + obwod);
}
}
W powyższym przykładzie mamy przeciążony operator "+" i "-". W przypadku operatora dodawania dodajemy do siebie długości boków natomiast w przypadku operatora odejmowania odejmujemy od każdego boku określoną liczbę, w naszym przypadku jest to 2.
Więcej na temat przeciążania operatorów:
http://msdn.microsoft.com/pl-pl/library/8edha89s.aspx
Komentarze facebook (polub nasz profil na FB aby je zobaczyć):