Przeciążanie operatorów
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 36 z 70.
Przeciążanie operatorów pozwala określić, jak operatory takie jak +, - czy == działają z Twoimi własnymi klasami. Zamiast wywoływać metody, takie jak point1.Add(point2), możesz zapisać to w bardziej naturalny sposób: point1 + point2.
Aby przeciążyć operator, utwórz specjalną metodę statyczną, używając słowa kluczowego operator:
public class Vector
{
public int X { get; }
public int Y { get; }
public Vector(int x, int y)
{
X = x;
Y = y;
}
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.X + b.X, a.Y + b.Y);
}
}Teraz możesz w naturalny sposób dodawać wektory:
Vector v1 = new Vector(3, 4);
Vector v2 = new Vector(1, 2);
Vector v3 = v1 + v2; // X = 4, Y = 6Metoda musi być public static, a co najmniej jeden parametr musi być typu zawierającego tę metodę. Możesz przeciążać operatory arytmetyczne (+, -, *, /), operatory porównania (==, !=, <, >) oraz operatory jednoargumentowe (-, !, ++).
Pamiętaj, że operatory porównania muszą być przeciążane parami — jeśli zdefiniujesz ==, musisz również zdefiniować !=.
Używaj przeciążania operatorów, gdy sprawia, że Twój kod jest bardziej intuicyjny, szczególnie w przypadku typów matematycznych, takich jak wektory, liczby zespolone czy pieniądze. Unikaj go, gdy znaczenie operacji nie jest od razu oczywiste.
Wyzwanie
ŁatwyStwórzmy klasę Fraction, dzięki której praca z ułamkami będzie naturalna za sprawą przeciążania operatorów. Zamiast wywoływać metody, takie jak fraction1.Add(fraction2), będzie można zapisać intuicyjne fraction1 + fraction2.
Podzielisz kod na dwa pliki:
Fraction.cs: Utwórz klasęFractionw przestrzeni nazwMath, która reprezentuje ułamek z licznikiem i mianownikiem. Ułamek powinien mieć:- Dwie tylko do odczytu właściwości typu całkowitego:
NumeratoriDenominator - Konstruktor, który przyjmuje licznik i mianownik (w tej kolejności)
- Przeciążony operator
+, który dodaje dwa ułamki według wzoru:(a/b) + (c/d) = (a*d + c*b) / (b*d) - Przeciążony operator
*, który mnoży dwa ułamki:(a/b) * (c/d) = (a*c) / (b*d) - Przeciążony operator
==, który zwracatrue, jeśli dwa ułamki są równe (porównaj je przez mnożenie krzyżowe:a*d == c*b) - Przeciążony operator
!=(wymagany przy przeciążaniu==), który zwraca wartość przeciwną do== - Metodę
GetDisplay(), która zwraca ułamek jako ciąg znaków w formacie"{Numerator}/{Denominator}"
- Dwie tylko do odczytu właściwości typu całkowitego:
Program.cs: W pliku głównym utwórz dwa ułamki na podstawie wartości wejściowych. Zademonstruj działanie przeciążonych operatorów, dodając i mnożąc ułamki oraz sprawdzając, czy są równe. Wypisz wyniki, aby pokazać, jak naturalnie działają te operatory.
Otrzymasz cztery dane wejściowe:
- Licznik pierwszego ułamka (liczba całkowita)
- Mianownik pierwszego ułamka (liczba całkowita)
- Licznik drugiego ułamka (liczba całkowita)
- Mianownik drugiego ułamka (liczba całkowita)
Wypisz wynik w tym formacie:
Fraction 1: {GetDisplay()}
Fraction 2: {GetDisplay()}
Sum: {GetDisplay()}
Product: {GetDisplay()}
Are equal: {True/False}Na przykład jeśli dane wejściowe to 1, 2, 2 i 4, wynik powinien wyglądać następująco:
Fraction 1: 1/2
Fraction 2: 2/4
Sum: 8/8
Product: 2/8
Are equal: TrueZwróć uwagę, że 1/2 i 2/4 są uznawane za równe, ponieważ 1*4 == 2*2. Suma i iloczyn są obliczane bez skracania — w tym wyzwaniu to w porządku. Najważniejsze jest to, że teraz możesz tak naturalnie wykonywać działania na ułamkach za pomocą znanych operatorów!
Spróbuj swoich sił
using System;
using Math;
class Program
{
public static void Main(string[] args)
{
// Wczytaj dane wejściowe dla pierwszego ułamka
int num1 = Convert.ToInt32(Console.ReadLine());
int den1 = Convert.ToInt32(Console.ReadLine());
// Wczytaj dane wejściowe dla drugiego ułamka
int num2 = Convert.ToInt32(Console.ReadLine());
int den2 = Convert.ToInt32(Console.ReadLine());
// TODO: Utwórz dwa obiekty Fraction, używając wczytanych wartości
// TODO: Użyj przeciążonego operatora +, aby dodać ułamki
// TODO: Użyj przeciążonego operatora *, aby pomnożyć ułamki
// TODO: Użyj przeciążonego operatora ==, aby sprawdzić równość
// TODO: Wyświetl wynik w wymaganym formacie:
// Ułamek 1: {GetDisplay()}
// Ułamek 2: {GetDisplay()}
// Suma: {GetDisplay()}
// Iloczyn: {GetDisplay()}
// Czy są równe: {True/False}
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online