Menu
Coddy logo textTech

Перегрузка операторов

Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 36 из 70.

Перегрузка операторов позволяет вам определить, как такие операторы, как +, - или ==, ведут себя с вашими пользовательскими классами. Вместо вызова таких методов, как point1.Add(point2), вы можете написать более естественное point1 + point2.

Чтобы перегрузить оператор, вы создаете специальный статический метод, используя ключевое слово 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);
    }
}

Теперь вы можете складывать векторы естественным образом:

Vector v1 = new Vector(3, 4);
Vector v2 = new Vector(1, 2);
Vector v3 = v1 + v2;  // X = 4, Y = 6

Метод должен быть public static, и как минимум один параметр должен быть содержащим типом. Вы можете перегружать арифметические операторы (+, -, *, /), операторы сравнения (==, !=, <, >) и унарные операторы (-, !, ++).

Обратите внимание, что операторы сравнения должны быть перегружены парами — если вы определяете ==, вы также должны определить !=.

Используйте перегрузку операторов, когда это делает ваш код более интуитивно понятным, особенно для математических типов, таких как векторы, комплексные числа или денежные суммы. Избегайте её, когда значение операции не является очевидным сразу.

challenge icon

Задание

Легко

Давайте создадим класс Fraction, который сделает работу с дробями естественной благодаря перегрузке операторов. Вместо вызова методов вроде fraction1.Add(fraction2), вы сможете писать интуитивно понятное fraction1 + fraction2.

Вы организуете свой код в двух файлах:

  • Fraction.cs: Создайте класс Fraction в пространстве имен Math, который представляет дробь с числителем и знаменателем. Ваша дробь должна иметь:
    • Два целочисленных свойства только для чтения: Numerator и Denominator
    • Конструктор, который принимает числитель и знаменатель (именно в таком порядке)
    • Перегруженный оператор +, который складывает две дроби по формуле: (a/b) + (c/d) = (a*d + c*b) / (b*d)
    • Перегруженный оператор *, который перемножает две дроби: (a/b) * (c/d) = (a*c) / (b*d)
    • Перегруженный оператор ==, который возвращает true, если две дроби равны (используйте перекрестное умножение для сравнения: a*d == c*b)
    • Перегруженный оператор != (требуется при перегрузке ==), который возвращает значение, противоположное ==
    • Метод GetDisplay(), который возвращает дробь в виде строки в формате "{Numerator}/{Denominator}"
  • Program.cs: В основном файле создайте две дроби, используя входные значения. Продемонстрируйте работу перегруженных операторов, сложив их, перемножив и проверив на равенство. Выведите результаты, чтобы показать естественную работу ваших операторов.

Вы получите четыре входных значения:

  • Числитель первой дроби (целое число)
  • Знаменатель первой дроби (целое число)
  • Числитель второй дроби (целое число)
  • Знаменатель второй дроби (целое число)

Выведите результат в следующем формате:

Fraction 1: {GetDisplay()}
Fraction 2: {GetDisplay()}
Sum: {GetDisplay()}
Product: {GetDisplay()}
Are equal: {True/False}

Например, если входные данные 1, 2, 2 и 4, вывод должен быть следующим:

Fraction 1: 1/2
Fraction 2: 2/4
Sum: 8/8
Product: 2/8
Are equal: True

Обратите внимание, что 1/2 и 2/4 признаются равными, так как 1*4 == 2*2. Сумма и произведение вычисляются без упрощения — для этой задачи это нормально. Прелесть в том, насколько естественно теперь можно работать с дробями, используя привычные операторы!

Шпаргалка

Перегрузка операторов позволяет определять пользовательское поведение для таких операторов, как +, -, *, ==, при работе с вашими классами.

Чтобы перегрузить оператор, создайте метод public static, используя ключевое слово 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);
    }
}

Использование:

Vector v1 = new Vector(3, 4);
Vector v2 = new Vector(1, 2);
Vector v3 = v1 + v2;  // X = 4, Y = 6

Вы можете перегружать:

  • Арифметические операторы: +, -, *, /
  • Операторы сравнения: ==, !=, <, > (должны перегружаться парами)
  • Унарные операторы: -, !, ++

При перегрузке == вы также должны перегрузить !=:

public static bool operator ==(Vector a, Vector b)
{
    return a.X == b.X && a.Y == b.Y;
}

public static bool operator !=(Vector a, Vector b)
{
    return !(a == b);
}

Попробуйте сами

using System;
using Math;

class Program
{
    public static void Main(string[] args)
    {
        // Считать входные данные для первой дроби
        int num1 = Convert.ToInt32(Console.ReadLine());
        int den1 = Convert.ToInt32(Console.ReadLine());
        
        // Считать входные данные для второй дроби
        int num2 = Convert.ToInt32(Console.ReadLine());
        int den2 = Convert.ToInt32(Console.ReadLine());
        
        // TODO: Создать два объекта Fraction, используя введенные значения
        
        // TODO: Использовать перегруженный оператор +, чтобы сложить дроби
        
        // TODO: Использовать перегруженный оператор *, чтобы перемножить дроби
        
        // TODO: Использовать перегруженный оператор == для проверки равенства
        
        // TODO: Вывести результат в требуемом формате:
        // Fraction 1: {GetDisplay()}
        // Fraction 2: {GetDisplay()}
        // Sum: {GetDisplay()}
        // Product: {GetDisplay()}
        // Are equal: {True/False}
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование