Главная страница
Навигация по странице:

  • Основы перегрузки операторов

  • Перегрузка бинарных операторов

  • Перегрузка унарных операторов

  • Справочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией


    Скачать 5.05 Mb.
    НазваниеСправочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией
    АнкорC #.pdf
    Дата08.12.2017
    Размер5.05 Mb.
    Формат файлаpdf
    Имя файлаC #.pdf
    ТипСправочник
    #10795
    страница14 из 52
    1   ...   10   11   12   13   14   15   16   17   ...   52
    Глава 9
    Перегрузка операторов

    Глава 9. Перегрузка операторов
    225 зык C# позволяет определить значение оператора относительно создаваемого класса.
    Этот процесс называется перегрузкой операторов. Перегружая оператор, вы расширяете его использование для класса. Результат действия оператора полностью находится в ваших руках, и может быть разным при переходе от класса к классу. Например, класс, который определяет связный список, может использовать оператор “
    +
    ” для добавления объектов в список. Класс, который реализует стек, может использовать оператор “
    +
    ” для занесения объекта в стек. А какой-то другой класс может использовать этот оператор иным способом.
    При перегрузке оператора ни одно из его исходных значений не теряется. Перегрузку оператора можно расценивать как введение новой операции для класса. Следовательно, перегрузка оператора “
    +
    ”, например, для обработки связного списка (в качестве оператора сложения) не изменяет его значение применительно к целым числам.
    Главное достоинство перегрузки операторов состоит в том, что она позволяет бесшовно интегрировать новый тип класса со средой программирования. Эта
    расширяемость типов — важная составляющая мощи таких объектно-ориентированных языков программирования, как C#. Если для класса определены некоторые операторы, вы можете оперировать объектами этого класса, используя обычный C#-синтаксис выражений.
    Более того, вы можете использовать в выражениях объект, включающий другие типы данных. Перегрузка операторов — одно из самых мощных средств языка C#.
    Основы перегрузки операторов
    Перегрузка операторов тесно связана с перегрузкой методов. Для перегрузки операторов используется ключевое слово operator, позволяющее создать операторный
    метод, который определяет действие оператора, связанное с его классом.
    Существует две формы методов operator: одна используется для унарных операторов, а другая — для бинарных. Общий же формат (для обоих случаев) таков:
    // Общий формат перегрузки для унарного оператора. public static
    тип_возврата
    operator
    ор
    (
    тип_параметра
    операнд
    )
    {
    // операции
    }
    // Общий формат перегрузки для бинарного оператора. public static
    тип_возврата
    operator
    ор
    (
    тип_параметра1
    операнд1
    ,
    тип_параметра2
    операнд2
    )
    {
    // операции
    }
    Здесь элемент
    op
    - это оператор (например “
    +
    ” или “
    /
    ”), который перегружается.
    Элемент
    тип_возврата
    это тип значения, возвращаемого при выполнении заданной операции. Несмотря на то что можно выбрать любой тип, тип возвращаемого значения чаще всего будет совпадать с типом класса, для которого этот оператор перегружается.
    Такая корреляция облегчает использование перегруженного оператора в выражениях. Для унарных операторов операнд передается в элементе
    операнд
    , а для бинарных — в элементах
    операнд1
    и
    операнд2
    .
    Для унарных операторов тип операнда должен совпадать с классом, для которого определен оператор. Что касается бинарных операторов, то тип хотя бы одного операнда
    Я

    226
    Часть I. Язык C# должен совпадать с соответствующим классом. Таким образом, C#-операторы нельзя перегружать для классов, не созданных вами. Например, вы не можете перегрузить оператор “
    +
    ” для типов int или string
    И последнее: параметры операторов не должны использовать модификатор ref или out
    Перегрузка бинарных операторов
    Чтобы разобраться, как работает перегрузка операторов, начнем с примера, в котором перегружаются два бинарных оператора — “
    +
    ” и “
    -
    “. В следующей программе создается класс
    ThreeD
    поддержки координат объекта в трехмерном пространстве. Перегруженный оператор “
    +
    ” выполняет сложение отдельных координат двух
    ThreeD
    -объектов, а перегруженный оператор “
    -
    “ вычитает координаты одного
    ThreeD
    -объекта из координат другого.
    // Пример перегрузки операторов. using System;
    // Класс трехмерных координат. class ThreeD { int x, y, z; // 3-х-мерные координаты. public ThreeD() { x = y = z = 0;
    } public ThreeD(int i, int j, int k) { x = i; y = j; z = k;
    }
    //
    Перегрузка бинарного оператора "+". public static ThreeD operator +(ThreeD op1,
    ThreeD op2)
    {
    ThreeD result = new ThreeD();
    /*
    Суммирование координат двух точек и возврат результата. */ result.x = op1.x + op2.x; // Эти операторы выполняют result.y = op1.y + op2.y; // целочисленное сложение. result.z = op1.z + op2.z; return result;
    }
    //
    Перегрузка бинарного оператора "-". public static ThreeD operator -(ThreeD op1, ThreeD op2) {
    ThreeD result = new ThreeD();
    /*
    Обратите внимание на порядок операндов. op1
    - левый операнд, op2 - правый. */ result.x = op1.x - op2.x; // Эти операторы выполняют result.y = op1.y - op2.y; // целочисленное вычитание. result.z = op1.z - op2.z; return result;
    }
    //
    Отображаем координаты X, Y, Z.

    Глава 9. Перегрузка операторов
    227 public void show() {
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class ThreeDDemo { public static void Main() {
    ThreeD a = new ThreeD(1, 2, 3);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD();
    Console.Write("Координаты точки a: "); a.show();
    Console.WriteLine();
    Console.Write("Координаты точки b: "); b.show();
    Console.WriteLine(); c = a + b; // Складываем а и b.
    Console.Write("Результат сложения а + b: "); c.show();
    Console.WriteLine(); c = a + b + c; // Складываем а, b и c.
    Console.Write("Результат сложения а + b + c: "); c.show();
    Console.WriteLine(); c = c - a; // Вычитаем а из c.
    Console.Write("Результат вычитания c - a: "); c.show();
    Console.WriteLine(); c = c - b; // Вычитаем b из c.
    Console.Write("Результат вычитания c - b: "); c.show();
    Console.WriteLine();
    }
    }
    При выполнении эта программа генерирует следующие результаты:
    Координаты точки a: 1, 2, 3
    Координаты точки b: 10, 10, 10
    Результат сложения а + b: 11, 12, 13
    Результат сложения а + b + c: 22, 24, 26
    Результат вычитания c - a: 21, 22, 23
    Результат вычитания c - b: 11, 12, 13
    Эту программу стоит рассмотреть подробнее. Начнем с перегруженного оператора

    +
    ”. При воздействии оператора “
    +
    ” на два объекта типа
    ThreeD
    величины соответствующих координат суммируются, как показано в методе operator+()
    . Однако заметьте, что этот метод не модифицирует значения ни одного из операндов. Этот метод

    228
    Часть I. Язык C# возвращает новый объект типа
    ThreeD
    , который содержит результат выполнения рассматриваемой операции. Это происходит и в случае стандартного арифметического оператора сложения “
    +
    ”, примененного, например, к числам 10 и 12. Результат операции
    10+12 равен 22, но при его получении ни 10, ни 12 не были изменены. Хотя не существует правила, которое бы не позволяло перегруженному оператору изменять значение одного из его операндов, все же лучше, чтобы он не противоречил общепринятым нормам.
    Обратите внимание на то, что метод operator+()
    возвращает объект типа
    ThreeD
    Несмотря на то что он мог бы возвращать значение любого допустимого в C# типа, тот факт, что он возвращает объект типа
    ThreeD
    , позволяет использовать оператор “
    +
    ” в таких составных выражениях, как а + b + c
    . Здесь часть этого выражения, a+b
    , генерирует результат типа
    ThreeD
    , который затем суммируется с объектом с. И если бы выражение генерировало значение иного типа (а не типа
    ThreeD
    ), такое составное выражение попросту не работало бы.
    И еще один важный момент. При сложении координат внутри метода operator+()
    выполняется целочисленное сложение, поскольку отдельные координаты представляют собой целочисленные величины. Факт перегрузки оператора “
    +
    ” для объектов типа
    ThreeD
    не влияет на оператор “
    +
    ”, применяемый к целым числам.
    Теперь рассмотрим операторный метод operator-()
    . Оператор "
    -
    " работает подобно оператору “
    +
    ” за исключением того, что здесь важен порядок следования операндов. Вспомните, что сложение коммутативно, а вычитание — нет (т.е. А-В не то же самое, что В-А). Для всех бинарных операторов первый параметр операторного метода будет содержать левый операнд, а второй параметр — правый. При реализации перегруженных версий некоммутативных операторов необходимо помнить, какой операнд является левым, а какой — правым.
    Перегрузка унарных операторов
    Унарные операторы перегружаются точно так же, как и бинарные. Главное отличие, конечно же, состоит в том, что в этом случае существует только один операнд. Рассмотрим, например, метод, который перегружает унарный “минус” для класса
    ThreeD
    // Перегрузка унарного оператора "-". public static ThreeD operator -(ThreeD op) {
    ThreeD result = new ThreeD(); result.x = -op.x; result.y = -op.y; result.z = -op.z; return result;
    }
    Здесь создается новый объект, который содержит поля операнда, но со знаком
    “минус”. Созданный таким образом объект и возвращается операторным методом operator-().
    Обратите внимание на то, что сам операнд остается немодифицированным. Такое поведение соответствует обычному действию унарного
    “минуса”. Например, в выражении а = -b а
    получает значение b
    , взятое с противоположным знаком, но само b
    при этом не меняется.

    Глава 9. Перегрузка операторов
    229
    Однако в двух случаях операторный метод изменяет содержимое операнда. Речь идет об операторах инкремента (
    ++
    ) и декремента (
    --
    ). Поскольку обычно (“в миру”) эти операторы выполняют функции инкрементирования и декрементирования значений, соответственно, то перегруженные операторы “
    +
    ” и “
    -
    “, как правило, инкрементируют свой операнд. Таким образом, при перегрузке этих операторов операнд обычно модифицируется.
    Например, рассмотрим метод operator++-()
    для класса
    ThreeD
    // Перегрузка унарного оператора "++". public static ThreeD operator ++(ThreeD op){
    //
    Оператор "++" модифицирует аргумент. op.x++; op.y++; op.z++; return op;
    }
    Обратите внимание: в результате выполнения этого операторного метода объект, на который ссылается операнд ор, модифицируется. Итак, операнд, подвергнутый операции

    ++
    ”, инкрементируется. Более того, модифицированный объект возвращается этим методом, благодаря чему оператор “
    ++
    ” можно использовать в более сложных выражениях.
    Ниже приведена расширенная версия предыдущего примера программы, которая, помимо прочего, демонстрирует определение и использование унарных операторов “
    -
    “ и

    ++
    ”.
    // Перегрузка большего числа операторов. using System;
    // Класс трехмерных координат. class ThreeD { int x, y, z; // 3-х-мерные координаты. public ThreeD() { x = y = z = 0;
    } public ThreeD(int i, int j, int k) { x = i; y = j; z = k;
    }
    //
    Перегрузка бинарного оператора "+". public static ThreeD operator +(ThreeD op1, ThreeD op2){
    ThreeD result = new ThreeD();
    /*
    Суммирование координат двух точек и возврат результата. */ result.x = op1.x + op2.x; result.y = op1.y + op2.y; result.z = op1.z + op2.z; return result;
    }
    //
    Перегрузка бинарного оператора "-". public static ThreeD operator -(ThreeD op1, ThreeD op2){
    ThreeD result = new ThreeD();
    /*
    Обратите внимание на порядок операндов.

    230
    Часть I. Язык C# op1
    - левый операнд, op2 - правый. */ result.x = op1.x - op2.x; result.y = op1.y - op2.y; result.z = op1.z - op2.z; return result;
    }
    //
    Перегрузка унарного оператора "-". public static ThreeD operator -(ThreeD op) {
    ThreeD result = new ThreeD(); result.x = -op.x; result.y = -op.y; result.z = -op.z; return result;
    }
    //
    Перегрузка унарного оператора "++". public static ThreeD operator ++(ThreeD op) {
    //
    Оператор "++" модифицирует аргумент. op.x++; op.y++; op.z++; return op;
    }
    //
    Отображаем координаты X, Y, Z. public void show(){
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class ThreeDDemo { public static void Main() {
    ThreeD a = new ThreeD(1, 2, 3);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD();
    Console.Write("Координаты точки a: "); a.show();
    Console.WriteLine();
    Console.Write("Координаты точки b: "); b.show();
    Console.WriteLine(); c = a + b; // Сложение а и b.
    Console.Write("Результат сложения а + b: "); c.show();
    Console.WriteLine(); c = a + b + c; // Сложение a, b и c.
    Console.Write("Результат сложения а + b + c: ");

    Глава 9. Перегрузка операторов
    231 c.show();
    Console.WriteLine(); c = c - a; // Вычитание а из c.
    Console.Write("Результат вычитания c - a: "); c.show();
    Console.WriteLine(); c = c - b; // Вычитание b из c.
    Console.Write("Результат вычитания c - b: "); c.show();
    Console.WriteLine(); c = -a; // Присваивание -а объекту c.
    Console.Write("Результат присваивания -a: "); c.show();
    Console.WriteLine(); a++;
    //
    Инкрементирование а.
    Console.Write("Результат инкрементирования a++: "); a.show();
    }
    }
    Результаты выполнения этой программы выглядят так:
    Координаты точки a: 1, 2, 3
    Координаты точки b: 10, 10, 10
    Результат сложения а + b: 11, 12, 13
    Результат сложения а + b + c: 22, 24, 26
    Результат вычитания c - a: 21, 22, 23
    Результат вычитания c - b: 11, 12, 13
    Результат присваивания -a: -1, -2, -3
    Результат инкрементирования a++: 2, 3, 4
    Как вы уже знаете, операторы “
    ++
    ” и “

    “ имеют как префиксную, так и постфиксную форму. Например, инструкции
    ++а; и a++; представляют собой допустимое использование оператора инкремента. Однако при перегрузке оператора “
    ++
    ” обе формы вызывают один и тот же метод. Следовательно, в этом случае невозможно отличить префиксную форму оператора “
    ++
    ” от постфиксной. Это касается и перегрузки оператора “

    “.

    232
    Часть I. Язык C#
    Выполнение операций над значениями
    встроенных C#-типов
    Для любого заданного класса и оператора любой операторный метод сам может перегружаться. Одна из самых распространенных причин этого — разрешить операции между объектами этого класса и другими (встроенными) типами данных. В качестве примера давайте снова возьмем класс
    ThreeD
    . Вы видели, как перегрузить оператор “
    +
    ”, чтобы он суммировал координаты одного
    ThreeD
    -объекта с координатами другого.
    Однако это не единственный способ определения операции сложения для класса
    ThreeD
    Например, может потребоваться суммирование какого-либо целого числа с каждой координатой
    ThreeD
    -объекта. Ведь тогда эту операцию можно использовать для смещения осей. Для ее реализации необходимо перегрузить оператор “
    +
    ”еще раз, например, так:
    // Перегружаем бинарный оператор "+" для суммирования
    // объекта и int-значения. public static ThreeD operator +(ThreeD op1, int op2){
    ThreeD result = new ThreeD(); result.x = op1.x + op2; result.y = op1.y + op2; result.z = op1.z + op2; return result;
    }
    Обратите внимание на то, что второй параметр имеет тип int
    . Таким образом, этот метод позволяет сложить int
    -значение с каждым полем
    ThreeD
    -объекта. Это вполне допустимо, поскольку, как разъяснялось выше, при перегрузке бинарного оператора тип только одного из его операндов должен совпадать с типом класса, для которого перегружается этот оператор. Другой операнд может иметь любой тип.
    Ниже приведена версия класса
    ThreeD
    , которая имеет два перегруженных метода operator+()
    /* Перегрузка оператора сложения для вариантов: объект + объект и объект + int-значение. */ using System;
    // Класс трехмерных координат. class ThreeD { int x, y, z; // 3-х-мерные координаты. public ThreeD() { x = y = z = 0;
    } public ThreeD(int i, int j, int k) { x = i; y = j; z = k;
    }
    //
    Перегружаем бинарный оператор "+" для варианта
    //
    "объект + объект". public static ThreeD operator +(ThreeD op1, ThreeD op2){
    ThreeD result = new ThreeD();
    /*
    Суммирование координат двух точек и возврат результата. */

    Глава 9. Перегрузка операторов
    233 result.x = op1.x + op2.x; result.y = op1.y + op2.y; result.z = op1.z + op2.z; return result;
    }
    //
    Перегружаем бинарный оператор "+" для варианта
    //
    "объект + int-значение". public static ThreeD operator +(ThreeD op1, int op2) {
    ThreeD result = new ThreeD(); result.x = op1.x + op2; result.y = op1.y + op2; result.z = op1.z + op2; return result;
    }
    //
    Отображаем координаты X, Y, Z. public void show() {
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class ThreeDDemo { public static void Main() {
    ThreeD a = new ThreeD(1, 2, 3);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD();
    Console.Write("Координаты точки a: "); a.show();
    Console.WriteLine();
    Console.Write("Координаты точки b: "); b.show();
    Console.WriteLine(); c = a + b; // объект + объект
    Console.Write("Результат сложения а + b: "); c.show();
    Console.WriteLine(); c = b + 10; // объект + int-значение
    Console.Write("Результат сложения b + 10: "); c.show();
    }
    }
    При выполнении программа генерирует следующие результаты:
    Координаты точки a: 1, 2, 3
    Координаты точки b: 10, 10, 10
    Результат сложения а + b: 11, 12, 13
    Результат сложения b + 10: 20, 20, 20

    234
    Часть I. Язык C#
    Как подтверждают результаты выполнения этой программы, если оператор “
    +
    ” применяется к двум объектам, их соответствующие координаты суммируются. А если оператор “
    +
    ” применяется к объекту и целому числу, то значения координат объекта увеличиваются на это целое число.
    Несмотря на то что приведенный выше способ перегрузки оператора “
    +
    ” существенным образом расширяет возможности класса
    ThreeD
    , работа на этом еще не окончена. И вот почему. Метод operator+(ThreeD, int)
    позволяет выполнять инструкции, подобные следующей. оb1 = оb2 + 10;
    Но, к сожалению, он не позволяет выполнять инструкции такого рода: оb1 = 10 + оb2;
    Дело в том, что целочисленное значение принимается в качестве второго аргумента, которым является правый операнд. А в предыдущей инструкции целочисленный аргумент находится слева. Чтобы сделать допустимыми две формы инструкций, необходимо перегрузить оператор “
    +
    ” еще раз. Новая версия должна будет в качестве первого параметра принимать значение типа int
    , а в качестве второго — объект типа
    ThreeD
    . И тогда старая версия метода operator+()
    будет обрабатывать вариант
    объект
    + int-
    значение
    ”, а новая — вариант “
    int-
    значение
    +
    объект
    ”. Перегрузка оператора “
    +

    (или любого другого бинарного оператора), выполненная подобным образом, позволит значению встроенного типа находиться слева или справа от оператора. Ниже приводится версия класса
    ThreeD
    , которая перегружает оператор “
    +
    ” с учетом описанных выше вариантов приема аргументов.
    /* Перегрузка оператора "+" для следующих вариантов: объект + объект, объект + int-значение и int-значение + объект. */ using System;
    // Класс трехмерных координат. class ThreeD { int x, y, z; // 3-х-мерные координаты. public ThreeD() { x = y = z = 0;
    } public ThreeD(int i, int j, int k) { x = i; y = j; z = k;
    }
    //
    Перегружаем бинарный оператор "+" для варианта
    //
    "объект + объект". public static ThreeD operator +(ThreeD op1, ThreeD op2){
    ThreeD result = new ThreeD();
    /*
    Суммирование координат двух точек и возврат результата. */ result.x = op1.x + op2.x; result.y = op1.y + op2.y; result.z = op1.z + op2.z; return result;
    }
    //
    Перегружаем бинарный оператор "+" для варианта
    //
    "объект + int-значение".

    Глава 9. Перегрузка операторов
    1   ...   10   11   12   13   14   15   16   17   ...   52


    написать администратору сайта