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

  • Перегрузка операторов отношений

  • Перегрузка операторов true и false

  • Перегрузка логических операторов

  • Простой случай перегрузки логических операторов

  • Включение операторов, действующих по сокращенной схеме вычислений

  • Операторы преобразования

  • Рекомендации и ограничения по созданию перегруженных операторов

  • Еще один пример перегрузки операторов

  • Справочник по 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
    страница15 из 52
    1   ...   11   12   13   14   15   16   17   18   ...   52
    235 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-значение + объект". public static ThreeD operator +(int op1, ThreeD op2) {
    ThreeD result = new ThreeD(); result.x = op2.x + op1; result.y = op2.y + op1; result.z = op2.z + op1; 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();
    Console.WriteLine(); c = 15 + b; // int-значение + объект
    Console.Write("Результат сложения 15 + b: "); c.show();
    }
    }

    236
    Часть I. Язык C#
    Вот результаты выполнения этой программы:
    Координаты точки a: 1, 2, 3
    Координаты точки b: 10, 10, 10
    Результат сложения а + b: 11, 12, 13
    Результат сложения b + 10: 20, 20, 20
    Результат сложения 15 + b: 25, 25, 25
    Перегрузка операторов отношений
    Операторы отношений (например, “
    ==
    ” или “
    <
    ”) также можно перегружать, причем сделать это совсем нетрудно. Как правило, перегруженный оператор отношения возвращает одно из двух возможных значений: true или false
    . Это позволяет использовать перегруженные операторы отношений в условных выражениях. Если бы они возвращали результат другого типа, это бы весьма ограничило круг их применения. Рассмотрим версию класса
    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 bool operator <(ThreeD op1, ThreeD op2){ if((op1.x < op2.x) && (op1.y < op2.y) &&
    (op1.z
    < op2.z)) return true; else return false;
    }
    //
    Перегрузка оператора ">". public static bool operator >(ThreeD op1, ThreeD op2){ if((op1.x > op2.x) && (op1.y > op2.y) &&
    (op1.z > op2.z)) return true; else return false;
    }
    //
    Отображаем координаты X, Y, Z. public void show()
    {

    Глава 9. Перегрузка операторов
    237
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class ThreeDDemo { public static void Main() {
    ThreeD a = new ThreeD(5, 6, 7);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD(1, 2, 3);
    Console.Write("Координаты точки a: "); a.show();
    Console.Write("Координаты точки b: "); b.show();
    Console.Write("Координаты точки c: "); c.show();
    Console.WriteLine(); if(a > c) Console.WriteLine("a > c - ИСТИНА"); if(a < c) Console.WriteLine("a < c - ИСТИНА"); if(a > b) Console.WriteLine("a > b - ИСТИНА"); if(a < b) Console.WriteLine("a < b - ИСТИНА");
    }
    }
    При выполнении эта программа генерирует такие результаты:
    Координаты точки a: 5, б, 7
    Координаты точки b: 10, 10, 10
    Координаты точки c: 1, 2, 3 а > c - ИСТИНА а < b - ИСТИНА
    На перегрузку операторов отношений налагается серьезное ограничение: их следует перегружать парами. Например, перегружая оператор “
    <
    ”, вы также должны перегрузить оператор “
    >
    ”, и наоборот. Вот что подразумевается под парами операторов отношений:
    ==
    !=
    <
    >
    <=
    >=
    Перегружая операторы “
    ==
    ” и “
    !=
    ”, следует перегрузить также методы
    Object.Equals()
    и
    Object.GetHashCode()
    . Эти методы (а также их перегрузка) рассматриваются в главе 11.
    Перегрузка операторов true и false
    Ключевые слова true и false в целях перегрузки также можно использовать в качестве унарных операторов. Перегруженные версии этих операторов обеспечивают специфическое определение понятий ИСТИНА и ЛОЖЬ в отношении создаваемых программистом классов. Если для класса реализовать таким образом ключевые слова true и false
    , то затем объекты этого класса можно использовать для управления инструкциями if
    , while
    , for и do-while
    , а также в
    ?
    -выражении. Их можно даже использовать для реализации специальных типов логики (например, нечеткой логики).

    238
    Часть I. Язык C#
    Операторы true и false должны быть перегружены в паре. Нельзя перегружать только один из них. Оба они выполняют функцию унарных операторов и имеют такой формат: public static bool operator true(
    тип_параметра
    op
    )
    {
    //
    Возврат значения true или false.
    } public static bool operator false(
    тип_параметра
    op
    ) {
    //
    Возврат значения true или false.
    }
    Обратите внимание на то, что каждая форма возвращает результат типа bool
    В следующем примере демонстрируется один из возможных способов реализации операторов true и false для класса
    ThreeD
    . Предполагается, что
    ThreeD
    -объект истинен, если по крайней мере одна его координата не равна нулю. Если все три координаты равны нулю, объект считается ложным. В целях демонстрации здесь также реализован оператор декремента.
    // Перегрузка операторов true и false для класса 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;
    }
    //
    Перегружаем оператор true. public static bool operator true(ThreeD op) { if((op.x !=0) || (op.y != 0) || (op.z != 0)) return true;
    //
    Хотя бы одна координата не равна 0. else return false;
    }
    //
    Перегружаем оператор false. public static bool operator false(ThreeD op) { if((op.x == 0) && (op.y == 0) && (op.z == 0)) return true;
    //
    Все координаты равны нулю. else return false;
    }
    //
    Перегружаем унарный оператор "--". public static ThreeD operator --(ThreeD op) { op.x--; op.y--; op.z--; return op;
    }
    //
    Отображаем координаты X, Y, Z.

    Глава 9. Перегрузка операторов
    239 public void show() {
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class TrueFalseDemo { public static void Main() {
    ThreeD a = new ThreeD(5, 6, 7);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD(0, 0, 0);
    Console.Write("Координаты точки a: "); a.show();
    Console.Write("Координаты точки b: "); b.show();
    Console.Write("Координаты точки c: "); c.show();
    Console.WriteLine(); if(a) Console.WriteLine("a - это ИСТИНА."); else Console.WriteLine("a - это ЛОЖЬ."); if(b) Console.WriteLine("b - это ИСТИНА."); else Console.WriteLine("b - это ЛОЖЬ."); if(c)
    Console.WriteLine("с - это ИСТИНА."); else
    Console.WriteLine("с - это ЛОЖЬ.");
    Console.WriteLine();
    Console.WriteLine(
    "Управляем циклом, используя объект класса ThreeD."); do
    { b.show(); b--;
    } while(b);
    }
    }
    Вот какие результаты генерирует эта программа:
    Координаты точки a: 5, 6, 7
    Координаты точки b: 10, 10, 10
    Координаты точки c: 0, 0, 0 а - это ИСТИНА. b - это ИСТИНА. c - ЭТО ЛОЖЬ.
    Управляем циклом, используя объект класса ThreeD.
    10, 10, 10 9, 9, 9 8, 8, 8 7, 7, 7 6, 6, 6 5, 5, 5 4, 4, 4 3, 3, 3 2, 2, 2 1, 1, 1

    240
    Часть I. Язык C#
    Обратите внимание на то, что объекты класса
    ThreeD
    используются для управления if
    -инструкциями и while
    -цикла. Что касается if
    -инструкций, то
    ThreeD
    -объект оценивается с помощью ключевого слова true
    . В случае истинности результата этой операции выполняется соответствующая инструкция. В случае do-while
    -цикла каждая его итерация декрементирует значение объект b
    . Цикл выполняется до тех пор, пока значение объекта b
    оценивается как ИСТИНА (т.е. содержит по крайней мере одну ненулевую координату). Когда все координаты объекта b
    станут равными нулю, он (объект) будет считаться ложным (благодаря оператору true
    ), и цикл прекратится.
    Перегрузка логических операторов
    Как вы знаете, в C# определены следующие логические операторы:
    &
    ,
    |
    ,
    !
    ,
    &&
    и
    ||
    Безусловно, перегруженными могут быть только
    &
    ,
    |
    ,
    !
    . Однако при соблюдении определенных правил можно использовать и операторы
    &&
    и
    ||
    , действующие по сокращенной схеме.
    Простой случай перегрузки логических операторов
    Начнем с рассмотрения простейшей ситуации. Если вы не планируете использовать логические операторы, работающие по сокращенной схеме, то можете перегружать операторы
    &
    и
    |
    по своему усмотрению, причем каждый вариант должен возвращать результат типа bool
    . Перегруженный оператор
    !
    также, как правило, возвращает результат типа bool
    Рассмотрим пример перегрузки логических операторов
    &
    ,
    |
    ,
    !
    для объектов типа
    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 bool operator |(ThreeD op1, ThreeD op2){ if( ((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |
    ((op2.x != 0) || (op2.y != 0) || (op2.z != 0)) ) return true; else return false;
    }
    //
    Перегрузка оператора "&". public static bool operator &(ThreeD op1, ThreeD op2)

    Глава 9. Перегрузка операторов
    241
    { if( ((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &
    ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) ) return true; else return false;
    }
    //
    Перегрузка оператора "!". public static bool operator !(ThreeD op)
    { if((op.x != 0) || (op.y != 0) || (op.z != 0)) return false; else return true;
    }
    //
    Отображаем координаты X, Y, Z. public void show()
    {
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class TrueFalseDemo { public static void Main() {
    ThreeD a = new ThreeD(5, 6, 7);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD(0, 0, 0);
    Console.Write("Координаты точки a: "); a.show();
    Console.Write("Координаты точки b: "); b.show();
    Console.Write("Координаты точки c: "); c.show();
    Console.WriteLine(); if(!a) Console.WriteLine("a - ЛОЖЬ."); if(!b) Console.WriteLine("b - ЛОЖЬ."); if(!c)
    Console.WriteLine("с - ложь.");
    Console.WriteLine(); if(a & b) Console.WriteLine("a & b - ИСТИНА."); else Console.WriteLine("a & b - ЛОЖЬ."); if(a & c) Console.WriteLine("a & с - ИСТИНА."); else Console.WriteLine("a & с - ЛОЖЬ."); if(a | b) Console.WriteLine("a | b - ИСТИНА."); else Console.WriteLine("a | b - ЛОЖЬ."); if(a | c) Console.WriteLine("a | с - ИСТИНА."); else Console.WriteLine("a | с - ЛОЖЬ.");
    }
    }
    При выполнении эта программа генерирует следующие результаты:
    Координаты точки a: 5, 6, 7
    Координаты точки b: 10, 10, 10

    242
    Часть I. Язык C#
    Координаты точки c: 0, 0, 0 c - ЛОЖЬ. а & b - ИСТИНА. а & с - ЛОЖЬ. a | b - ИСТИНА. а | с - ИСТИНА.
    В этом примере методы operator |()
    , operator &()
    и operator !()
    возвращают результат типа bool
    . Это необходимо в том случае, если перечисленные операторы должны использоваться в своем обычном “амплуа” (т.е. там, где ожидается результат типа bool
    ). Вспомните, что для всех встроенных типов результат выполнения логической операции представляет собой значение типа bool
    . Таким образом, вполне логично, что перегруженные версии этих операторов должны возвращать значение типа bool
    . К сожалению, такая логика работает в случае, если нет необходимости в операторах, работающих по сокращенной схеме вычислений.
    Включение операторов, действующих по сокращенной схеме
    вычислений
    Чтобы иметь возможность использовать операторы
    &&
    и
    ||
    , действующие по сокращенной схеме вычислений, необходимо соблюдать четыре правила. Во-первых, класс должен перегружать операторы
    &
    и
    |
    . Во-вторых,
    &- и
    |- методы должны возвращать объект класса, для которого перегружаются эти операторы. В-третьих, каждый параметр должен представлять собой ссылку на объект класса, для которого перегружаются эти операторы. В-четвертых, тот же класс должен перегружать операторы true и false
    . При соблюдении всех этих условий операторы сокращенной схемы действия автоматически становятся доступными для применения.
    В следующей программе показано, как реализовать операторы
    &
    и
    |
    для класса
    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){ if( ((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |
    ((op2.x != 0) || (op2.y != 0) || (op2.z != 0)) ) return new ThreeD(1, 1, 1); else return new ThreeD(0, 0, 0);

    Глава 9. Перегрузка операторов
    243
    }
    // Перегружаем оператор "&" для вычислений по
    // сокращенной схеме. public static ThreeD operator &(ThreeD op1, ThreeD op2) { if( ((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &
    ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) ) return new ThreeD(1, 1, 1); else return new ThreeD(0, 0, 0);
    }
    //
    Перегружаем оператор "!". public static bool operator !(ThreeD op)
    { if(op) return false; else return true;
    }
    //
    Перегружаем оператор true. public static bool operator true(ThreeD op) { if((op.x != 0) || (op.y != 0) || (op.z != 0)) return true;
    //
    Хотя бы одна координата не
    // равна нулю. else return false;
    }
    //
    Перегружаем оператор false. public static bool operator false(ThreeD op) { if((op.x == 0) && (op.y == 0) && (op.z == 0)) return true;
    //
    Все координаты равны нулю. else return false;
    }
    //
    Отображаем координаты X, Y, Z. public void show()
    {
    Console.WriteLine(x + ", " + y + ", " + z);
    }
    } class TrueFalseDemo { public static void Main() {
    ThreeD a = new ThreeD(5, 6, 7);
    ThreeD b = new ThreeD(10, 10, 10);
    ThreeD c = new ThreeD(0, 0, 0);
    Console.Write("Координаты точки a: "); a.show();
    Console.Write("Координаты точки b: "); b.show();
    Console.Write("Координаты точки c: "); c.show();
    Console.WriteLine();

    244
    Часть I. Язык C# if(a) Console.WriteLine("a - ИСТИНА."); if(b) Console.WriteLine("b - ИСТИНА."); if(c)
    Console.WriteLine("с - ИСТИНА."); if(!a) Console.WriteLine("a - ЛОЖЬ."); if(!b) Console.WriteLine("b - ЛОЖЬ."); if(!c)
    Console.WriteLine("с - ЛОЖЬ.");
    Console.WriteLine();
    Console.WriteLine("Используем операторы & и !"); if(a & b) Console.WriteLine("a & b - ИСТИНА."); else Console.WriteLine("a & b - ЛОЖЬ."); if(a & c) Console.WriteLine("a & с - ИСТИНА."); else Console.WriteLine("a & с - ЛОЖЬ."); if(a | b) Console.WriteLine("a | b - ИСТИНА."); else Console.WriteLine("a | b - ЛОЖЬ."); if(a | c) Console.WriteLine("a | с - ИСТИНА."); else Console.WriteLine("a | с - ЛОЖЬ.");
    Console.WriteLine();
    //
    Теперь используем операторы &.& и ||, действующие
    // по сокращенной схеме вычислений.
    Console.WriteLine(
    "Используем \"сокращенные\" операторы && и ||"); if(a && b) Console.WriteLine("а && b - ИСТИНА."); else Console.WriteLine("a && b - ЛОЖЬ."); if(a && c) Console.WriteLine("a && с - ИСТИНА."); else Console.WriteLine("a && с - ЛОЖЬ."); if(a || b) Console.WriteLine("a || b - ИСТИНА."); else Console.WriteLine("a || b - ЛОЖЬ."); if(a || c) Console.WriteLine("a || c - ИСТИНА."); else Console.WriteLine("a || c - ЛОЖЬ.");
    }
    }
    Эта программа при выполнении генерирует такие результаты:
    Координаты точки a: 5, 6, 7
    Координаты точки b: 10, 10, 10
    Координаты точки c: 0, 0, 0 а - ИСТИНА. b - ИСТИНА. c - ЛОЖЬ.
    Используем операторы & и | а & b - ИСТИНА. a & c - ЛОЖЬ. а | b - ИСТИНА. а | c - ИСТИНА.

    Глава 9. Перегрузка операторов
    245
    Используем "сокращенные" операторы && и || а && b - ИСТИНА. а && с - ЛОЖЬ. а || b - ИСТИНА. a || с - ИСТИНА.
    Теперь остановимся подробнее на реализации операторов
    &
    и
    |
    . Для удобства приведем здесь их операторные методы.
    // Перегружаем оператор "|" для вычислений по
    // сокращенной схеме. public static ThreeD operator |(ThreeD op1, ThreeD op2)
    { if( ((op1.x != 0) || {op1.y != 0) || (op1.z != 0)) |
    ((op2.x != 0) || (op2.y != 0) || (op2.z != 0)) ) return new ThreeD(1, 1, 1); else return new ThreeD(0, 0, 0);
    }
    //
    Перегружаем оператор "&" для вычислений по
    // сокращенной схеме. public static ThreeD operator &(ThreeD op1, ThreeD op2)
    { if( ((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &
    ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) ) return new ThreeD(1, 1, 1); else return new ThreeD(0, 0, 0);
    }
    Обратите внимание на то, что оба операторных метода сейчас возвращают объект типа
    ThreeD
    , а также на то, как генерируется этот объект. Если результатом операции оказывается значение ИСТИНА, то создается и возвращается истинный
    ThreeD
    -объект
    (т.е. объект, в котором не равна нулю хотя бы одна координата). Если же результатом операции оказывается значение ЛОЖЬ, то создается и возвращается ложный
    ThreeD
    - объект (т.е. объект, в котором равны нулю все координаты). Таким образом, в инструкции if(а & b) Console.WriteLine("a & b - ИСТИНА."); else Console.WriteLine("a & b - ЛОЖЬ."); результатом операции а & b является
    ThreeD
    -объект, который в данном случае оказывается истинным. Поскольку в классе
    ThreeD
    определены операторы true и false
    , результирующий объект подвергается воздействию оператора true
    , вследствие чего возвращается результат типа bool
    . В данном случае результат равен значению true, и поэтому инструкция выводит сообщение "а & b - ИСТИНА."
    Поскольку в этом примере программы все необходимые правила соблюдены, для объектов класса
    ThreeD
    теперь доступны логические операторы сокращенного действия.
    Их работа заключается в следующем. Первый операнд тестируется с помощью операторного метода operator true
    (для оператора “
    ||
    ”) или операторного метода operator false
    (для оператора “
    &&
    ”). Если этот тест в состоянии определить результат всего выражения, то оставшиеся
    &
    - или
    |
    -операции уже не выполняются. В противном случае для определения результата используется соответствующий перегруженный оператор “
    &
    ” или

    |
    ”. Таким образом, использование
    &&
    - или
    ||
    -операторов приводит к выполнению соответствующих
    &
    - или
    |
    -операций только в том случае, когда первый операнд не предопределяет результат всего выражения. Рассмотрим, например, следующую инструкцию из нашей программы:

    246
    Часть I. Язык C# if(a || с) Console.WriteLine("a || с - ИСТИНА.");
    Сначала к объекту а
    применяется оператор true
    . Поскольку в данной ситуации а
    — истинный объект, в использовании операторного
    |
    -метода необходимости нет. Но эту инструкцию можно переписать и по-другому: if(с || a) Console.WriteLine("с || а - ИСТИНА.");
    В этом случае оператор true сначала будет применен к объекту с
    , а он в данной ситуации является ложным. Тогда будет вызван операторный
    |
    -метод, чтобы определить, является ли истинным объект а
    , что здесь как раз соответствует действительности.
    Хотя на первый взгляд может показаться, что метод, используемый для разрешения использовать операторы сокращенного действия, несколько усложнен, то при более внимательном рассмотрении нетрудно убедиться в его логичности. Перегружая операторы true и false для класса, вы позволяете компилятору использовать операторы сокращенного действия без их перегрузки в явном виде. Более того, вы получаете возможность применять объекты в условных выражениях. Поэтому, если вам не нужна узкопрофильная реализация операторов “
    &
    ” или “
    |
    ”, то лучше всего делать это в полном объеме.
    Операторы преобразования
    Иногда объект класса нужно использовать в выражении, включающем другие типы данных. Такие средства может обеспечить перегрузка одного или нескольких операторов.
    Но в некоторых случаях желаемого результата можно достичь за счет преобразования типов
    (из классового в нужный). В целях обработки подобных ситуаций C# позволяет создавать специальный тип операторного метода operator
    , именуемого
    оператором
    преобразования. Такой оператор преобразует объект некоторого класса в значение другого типа. По сути, оператор преобразования перегружает оператор приведения типов.
    Операторы преобразования способствуют полной интеграции классовых типов в C#-среду программирования, позволяя объектам класса свободно смешиваться с данными других типов при условии определения операторов преобразования в эти “другие типы”.
    Существуют две формы операторов преобразования: явная и неявная. В общем виде они записываются так: public static explicit operator
    тип_результата
    (
    исходный_тип
    v
    )[return
    значение
    ;] public static implicit operator тип_результата(
    исходный_тип
    v
    )[return
    значение
    ;]
    Здесь элемент
    тип_результата
    представляет собой тип, в который вы хотите выполнить преобразование; элемент
    исходный_тип
    означает тип объекта, подлежащего преобразованию; элемент
    v
    — значение класса после преобразования. Операторы преобразования возвращают данные типа
    тип_результата
    , причем спецификатор типа здесь указывать не разрешается.
    Если оператор преобразования определен с ключевым словом implicit
    , преобразование выполняется автоматически, т.е. при использовании объекта в выражении, включающем данные типа
    тип_результата
    . Если оператор преобразования определен с ключевым словом explicit
    , преобразование выполняется при использовании оператора приведения типов. Для одной и той же пары типов, участвующих в преобразовании, нельзя определить как explicit
    -, так и implicit
    -оператор преобразования.
    Для иллюстрации определения и использования оператора преобразования создадим его для класса
    ThreeD
    . Предположим, необходимо преобразовать объект типа

    Глава 9. Перегрузка операторов
    247
    ThreeD
    в целочисленное значение, чтобы его можно было использовать в выражениях типа int
    . Это преобразование будет заключаться в вычислении произведения значений всех трех координат объекта. Для реализации такого преобразования используем implicit
    -форму оператора, который будет иметь такой вид: public static implicit operator int(ThreeD op1)
    { return op1.x * op1.y * op1.z;
    }
    Ниже приведен текст программы, в которой иллюстрируется использование этого оператора преобразования.
    // Пример использования implicit-оператора преобразования. 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;
    }
    //
    Неявное преобразование из типа ThreeD в тип int. public static implicit operator int(ThreeD op1) { return op1.x * op1.y * op1.z;
    }
    //
    Отображаем координаты 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(); int i;
    Console.Write("Координаты точки a: "); a.show();

    248
    Часть I. Язык C#
    Console.WriteLine();
    Console.Write("Координаты точки b: "); b.show();
    Console.WriteLine(); c = a + b; // Суммируем координаты точек а и b.
    Console.Write("Результат сложения а + b: "); c.show();
    Console.WriteLine(); i = a; // Преобразуем в значение типа int.
    Console.WriteLine(
    "Результат присваивания i = a: " + i);
    Console.WriteLine(); i = a * 2 - b; // Преобразуем в значение типа int.
    Console.WriteLine(
    "Результат вычисления выражения а * 2 - b: " + i);
    }
    }
    При выполнении эта программа генерирует следующие результаты:
    Координаты точки a: 1, 2, 3
    Координаты точки b: 10, 10, 10
    Результат сложения а + b: 11, 12, 13
    Результат присваивания i = a: 6
    Результат вычисления выражения а * 2 - b: -988
    Как видно по результатам выполнения этой программы, если объект класса
    ThreeD
    используется в выражении целочисленного типа (например i = а
    ), к этому объекту применяется оператор преобразования. В данном случае результатом этого преобразования будет число 6, полученное при умножении всех координат, хранимых в объекте а
    . Но если для выражения не требуется преобразование в int
    -значение, оператор преобразования не вызывается. Поэтому при вычислении выражения c = а + b операторный метод operator int()
    не вызывается.
    Можно создавать различные методы преобразования, отвечающие вашим потребностям. Например, можно определить операторный метод преобразования объекта какого-либо класса в double
    - или long
    -значение. При этом каждое преобразование выполняется автоматически и независимо от других.
    Оператор неявного преобразования применяется автоматически в том случае, когда в выражении требуется преобразование, когда методу передается объект, когда выполняется присваивание, а также когда используется явно заданная операция приведения объекта к результирующему типу. В качестве альтернативного варианта можно создать оператор явного преобразования, который вызывается только в случае явного приведения типов.
    Оператор явного преобразования не вызывается автоматически. Вот, например, как выглядит предыдущая программа, переработанная для использования оператора явного преобразования объекта в значение типа int
    :
    // Использование оператора явного преобразования. using System;
    // Класс трехмерных координат.

    Глава 9. Перегрузка операторов
    249 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;
    }
    //На этот раз перегружаем explicit-оператор. public static explicit operator int(ThreeD op1)
    { return op1.x * op1.y * op1.z;
    }
    //
    Отображаем координаты 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(); int i;
    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(); i = (int) a; // Преобразуем объект в значение
    // типа int, поскольку явно задана
    // операция приведения типов.
    Console.WriteLine("Результат присваивания i = a: " + i);
    Console.WriteLine(); i = (int)a * 2 - (int)b; // Требуется приведение типов.

    250
    Часть I. Язык C#
    Console.WriteLine(
    "Результат вычисления выражения а * 2 - b: " + i);
    }
    }
    Поскольку оператор преобразования теперь определен с использованием ключевого слова explicit
    , преобразование объекта в значение типа int должно быть задано как оператор приведения типов. Например, если в строке кода i = (int) a; удалить оператор приведения типов, программа не скомпилируется.
    Определение и использование операторов преобразования связано с рядом ограничений.
    ■ Исходный тип объекта либо тип результата преобразования должен совпадать с создаваемым классом. Не разрешается переопределять такие преобразования, как из типа double в тип int
    ■ Нельзя определять преобразование в класс
    Object или из него.
    ■ Нельзя задавать как явное, так и неявное преобразование одновременно для одной и той же пары исходного и результирующего типов.
    ■ Нельзя задавать преобразование из базового класса в производный. (О базовых и производных классах см. главу 11.)
    ■ Нельзя задавать преобразование из одного интерфейса в другой. (Об интерфейсах см. главу 12.)
    Помимо перечисленных правил существуют также рекомендации, которым обычно следуют при выборе между явным и неявным операторами преобразования. Несмотря на определенные удобства к неявно заданным преобразованиям прибегают только в ситуациях, когда преобразование гарантированно лишено ошибок. Другими словами, неявные операторы преобразования следует создавать только при соблюдении следующих условий. Во-первых, такое преобразование должно гарантировать отсутствие потери данных, которое имеет место при усечении, переполнении или потере знака. Во-вторых, преобразование не должно стать причиной возникновения исключительных ситуаций. Если предполагаемое преобразование не отвечает этим требованиям, следует использовать преобразование явного типа.
    Рекомендации и ограничения по созданию
    перегруженных операторов
    Действие перегруженного оператора применительно к классу, для которого он определяется, не обязательно должно иметь отношение к стандартному действию этого оператора применительно к встроенным C#-типам. Но в целях структурированности и читабельности программного кода создаваемый перегруженный оператор должен по возможности отражать исходное назначение того или иного оператора. Например, оператор

    +
    ”, перегруженный для класса
    ThreeD
    , концептуально подобен оператору “
    +
    ”, определенному для целочисленных типов. Ведь вряд ли есть логика в определении для класса, например, оператора “
    +
    ”, который по своему действию больше напоминает оператор деления (
    /
    ). Таким образом, основная идея создания перегруженного оператора — наделить его новыми (нужными для вас) возможностями, которые тем не менее связаны с его первоначальным назначением.

    Глава 9. Перегрузка операторов
    251
    На перегрузку операторов налагается ряд ограничений. Нельзя изменять приоритет оператора. Нельзя изменять количество операндов, принимаемых оператором, хотя операторный метод мог бы игнорировать любой операнд. Некоторые операторы вообще нельзя перегружать. Например, нельзя перегружать какие бы то ни было операторы присваивания (включая составные, например “
    +=
    ”). Ниже перечислены остальные операторы, перегрузка которых запрещена.
    && || [] () new is sizeof typeof ?
    ->
    =
    Несмотря на то что нельзя перегружать оператор приведения типов (
    ()
    ) в явном виде, можно создать операторы преобразования, которые, как было показано выше, успешно выполняют это.
    Может показаться серьезным ограничением запрещение перегрузки таких составных операторов присваивания, как “
    +=
    ”. Если вы определили оператор, который используется в составном операторе присваивания, будет вызван соответствующий перегруженный операторный метод. Таким образом, использование “
    +=
    ” в программе автоматически вызывает вашу версию метода operator+()
    . Например, возьмем снова класс
    ThreeD
    При использовании фрагмента кода
    ThreeD а = new ThreeD(1, 2, 3);
    ThreeD b = new ThreeD(10, 10, 10); b += a; // Суммируем а и b. автоматически вызывается метод operator+()
    класса
    ThreeD
    , в результате чего объект b
    будет содержать координаты 11,12,13.
    И еще. Несмотря на то что нельзя перегружать оператор доступа к элементам массива по индексу (
    []
    ), используя операторный метод (
    operator()
    ), можно создавать индексаторы, которые описаны в следующей главе.
    Еще один пример перегрузки операторов
    На протяжении всей этой главы для демонстрации перегрузки операторов мы использовали класс
    ThreeD
    . Но прежде чем завершить эту главу, хотелось бы рассмотреть еще один пример. Несмотря на то что основные принципы перегрузки операторов не зависят от используемого класса, следующий пример поможет продемонстрировать мощь перегрузки операторов, особенно в случаях, связанных с расширяемостью типов.
    В этом примере программы разрабатывается четырехразрядный целочисленный тип данных, для которого определяется ряд операций. Возможно, вам известно, что на заре компьютерной эры четырехразрядный тип был широко распространен, поскольку он представлял половину байта. Этот тип также позволял хранить одну шестнадцатеричную цифру. Так как четыре бита составляют половину байта, эту полубайтовую величину часто называли nybble. В период всеобщего распространения компьютеров с передней панелью, ввод данных в которые осуществлялся порциями объемом в 1 nybble, программисты привыкли оперировать величинами такого размера. Несмотря на то что полубайт нынче утратил былую популярность, этот четырехразрядный тип представляет интерес в качестве дополнения к другим целочисленным типам данных. По традиции nybble-значение рассматривается как значение без знака.
    В следующем примере используется класс
    Nybble
    , в котором реализуется полубайтовый тип данных. Он основан на встроенном типе int
    , но ограничивает допустимые для хранения значения диапазоном 0—15. В этом классе определяются следующие операторы:
    ■ сложение
    Nybble
    -объекта с
    Nybble
    -объектом;

    252
    Часть I. Язык C#
    ■ сложение int
    -значения с
    Nybble
    -объектом;
    ■ сложение
    Nybble
    -объекта с int-значением;
    ■ больше (
    >
    ) и меньше (
    <
    );
    ■ оператор инкремента;
    ■ преобразование int
    -значения в
    Nybble
    -объект;
    ■ преобразование
    Nybble
    -объекта в int
    -значение.
    Этих операций вполне достаточно, чтобы показать, насколько полно тип класса может быть интегрирован с системой типов C#. Однако для полноты реализации типа
    Nybble
    , необходимо определить остальные операции. Это предлагается сделать читателю в качестве упражнения.
    Ниже приведен код класса
    Nybble
    , а также класса
    NybbleDemo
    , который позволяет продемонстрировать его использование.
    // Создание 4-битового типа с именем Nybble. using System;
    // 4-битовый тип. class Nybble { int val; // Основа типа - встроенный тип int. public Nybble() { val = 0;
    } public Nybble(int i) { val = i; val = val & 0xF; // Выделяем младшие 4 бита.
    }
    //
    Перегружаем бинарный оператор "+" для
    // сложения Nybble + Nybble. public static Nybble operator +(Nybble op1, Nybble op2)
    {
    Nybble result = new Nybble(); result.val = op1.val + op2.val; result.val = result.val & 0xF;
    // Оставляем младшие
    //
    4 бита. return result;
    }
    //
    Перегружаем бинарный оператор "+" для
    // сложения Nybble + int. public static Nybble operator +(Nybble op1, int op2)
    {
    Nybble result = new Nybble(); result.val = op1.val + op2; result.val = result.val & 0xF;
    // Оставляем младшие
    //
    4 бита. return result;
    }

    Глава 9. Перегрузка операторов
    253
    // Перегружаем бинарный оператор "+" для
    // сложения int + Nybble. public static Nybble operator +(int op1, Nybble op2)
    {
    Nybble result = new Nybble(); result.val = op1 + op2.val; result.val = result.val & 0xF;
    // Оставляем младшие
    //
    4 бита. return result;
    }
    //
    Перегружаем оператор "++". public static Nybble operator ++(Nybble op)
    { op.val++; op.val = op.val & 0xF; // Оставляем младшие
    //
    4 бита. return op;
    }
    //
    Перегружаем оператор ">". public static bool operator >(Nybble op1, Nybble op2)
    { if(op1.val > op2.val) return true; else return false;
    }
    //
    Перегружаем оператор "<". public static bool operator <(Nybble op1, Nybble op2)
    { if(op1.val < op2.val) return true; else return false;
    }
    //
    Преобразование Nybble-объекта в значение типа int. public static implicit operator int(Nybble op)
    { return op.val;
    }
    //
    Преобразование int-значения в Nybble-объект. public static implicit operator Nybble(int op) { return new Nybble(op);
    }
    } class NybbleDemo { public static void Main() {
    Nybble a = new Nybble(1);
    Nybble b = new Nybble(10);
    Nybble c = new Nybble(); int t;
    Console.WriteLine("a: " + (int) a);

    254
    Часть I. Язык C#
    Console.WriteLine("b: " + (int) b);
    //
    Используем Nybble-объект в if-инструкции. if(a < b) Console.WriteLine("а меньше b\n");
    //
    Суммируем два Nybble-объекта. c = a + b;
    Console.WriteLine(
    "с после сложения c = a + b: " + (int) c);
    //
    Суммируем int-значение с Nybble-объектом. a += 5;
    Console.WriteLine("а после сложения а += 5: " + (int) a);
    Console.WriteLine();
    //
    Используем Nybble-объект в int-выражении. t = a * 2 + 3;
    Console.WriteLine(
    "Результат выражения а * 2 + 3: " + t);
    Console.WriteLine();
    //
    Иллюстрируем присваивание Nybble-объекту
    // int-значения и переполнение. a = 19;
    Console.WriteLine(
    "Результат присваивания а = 19: " + (int) a);
    Console.WriteLine();
    //
    Используем Nybble-объект для управления циклом.
    Console.WriteLine(
    "Управляем for-циклом с помощью Nybble-объекта."); for(a = 0; a < 10; a++)
    Console.Write((int) a + " ");
    Console.WriteLine();
    }
    }
    При выполнении эта программа генерирует следующие результаты: a: 1 b: 10 а меньше b с после сложения c = а + b: 11 а после сложения а += 5: 6
    Результат выражения а * 2 + 3: 15
    Результат присваивания а = 19: 3
    Управляем for-циклом с помощью Nybble-объекта. 0123456789
    Несмотря на то что большинство реализованных здесь операций не нуждается в дополнительных комментариях, имеет смысл остановиться вот на чем. Операторы преобразования играют немаловажную роль в интеграции типа
    Nybble в систему типов

    Глава 9. Перегрузка операторов
    255
    C#. Поскольку в этом классе определены преобразования как из
    Nybble
    -объекта в int
    - значение, так из int
    -значения в
    Nybble
    -объект, то
    Nybble
    -объекты можно свободно смешивать в арифметических выражениях. Рассмотрим, например, такое выражение из этой программы: t = а * 2 + 3;
    Здесь переменная t
    имеет тип int
    , а переменная а
    представляет собой объект класса
    Nybble
    . Тем не менее эти два типа совместимы в одном выражении благодаря оператору неявного преобразования
    Nybble
    -объекта в int
    -значение. В данном случае, поскольку остальная часть выражения имеет тип int
    , объект а
    преобразуется в int
    -значение посредством вызова соответствующего метода преобразования.
    Преобразование int
    -значения в
    Nybble
    -объект позволяет присваивать
    Nybble
    - объекту int
    -значение. Например, инструкция а = 19; работает следующим образом. Сначала выполняется оператор преобразования int
    - значения в
    Nybble
    -объект. При этом создается новый
    Nybble
    -объект, который содержит младшие четыре (двоичных) разряда числа 19 (19 10
    = 10011 2
    ). Это приводит к переполнению, поскольку значение 19 выходит за пределы диапазона, допустимого в классе
    Nybble
    . В результате получаем число 3 (0011 2
    = 3 10
    ), которое и присваивается объекту а
    Без определения операторов преобразования такие выражения были бы недопустимы.
    Преобразование
    Nybble
    -объекта в int
    -значение используется также в цикле for
    Без определения этого преобразования было бы невозможно написать цикл for таким простым способом.

    Полный справочник по
    1   ...   11   12   13   14   15   16   17   18   ...   52


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