Текст книги "C# 4.0: полное руководство"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 21 (всего у книги 83 страниц)
Операторы отношения, например == и <, могут также перегружаться, причем очень просто. Как правило, перегруженный оператор отношения возвращает логическое значение true
и false
. Это вполне соответствует правилам обычного применения подобных операторов и дает возможность использовать их перегружаемые разновидности в условных выражениях. Если же возвращается результат другого типа, то тем самым сильно ограничивается применимость операторов отношения.
Ниже приведен очередной вариант класса ThreeD
, в котором перегружаются операторы < и >. В данном примере эти операторы служат для сравнения объектов ThreeD
, исходя из их расстояния до начала координат. Один объект считается больше другого, если он находится дальше от начала координат. А кроме того, один объект считается меньше другого, если он находится ближе к началу координат. Такой вариант реализации позволяет, в частности, определить, какая из двух заданных точек находится на большей сфере. Если же ни один из операторов не возвращает логическое значение true, то обе точки находятся на одной и той же сфере. Разумеется, возможны и другие алгоритмы упорядочения.
// Перегрузить операторы < и >.
using System;
// Класс для хранения трехмерных координат,
class ThreeD {
int x, y, z; // трехмерные координаты
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 (Math.Sqrt(op1.x * op1.x + op1.y * op1.y + op1.z * op1.z) <
Math.Sqrt(op2.x * op2.x + op2.y * op2.y + op2.z * op2.z))
return true;
else
return false;
}
// Перегрузить оператор >.
public static bool operator >(ThreeD op1, ThreeD op2) {
if (Math.Sqrt(op1.x * op1.x + op1.y * op1.y + op1.z * op1.z) >
Math.Sqrt(op2.x * op2.x + op2.y * op2.y + op2.z * op2.z))
return true;
else
return false;
}
// Вывести координаты X, Y, Z.
public void Show() {
Console.WriteLine(x + ", " + y + ", " + z) ;
}
}
class ThreeDDemo {
static void Main() {
ThreeD a = new ThreeD(5, 6, 7);
ThreeD b = new ThreeD(10, 10, 10);
ThreeD c = new ThreeD(1, 2, 3);
ThreeD d = new ThreeD(6, 7, 5);
Console.Write("Координаты точки a: ");
a.Show();
Console.Write("Координаты точки b: ");
b.Show();
Console.Write("Координаты точки с: ");
c.Show();
Console.Write("Координаты точки d: ");
d.Show();
Console.WriteLine();
if (a > c) Console.WriteLine(«а > с истинно»);
if (a < c) Console.WriteLine(«а < с истинно»);
if (a > b) Console.WriteLine(«а > b истинно»);
if (a < b) Console.WriteLine(«а < b истинно»);
if (a > d) Console.WriteLine(«а > d истинно»);
else if (a < d) Console.WriteLine(«a < d истинно»);
else Console.WriteLine("Точки a и d находятся на одном расстоянии " +
«от начала отсчета»);
}
}
Вот к какому результату приводит выполнение этого кода.
Координаты точки a: 5, 6, 7
Координаты точки b: 10, 10, 10
Координаты точки c: 1, 2, 3
Координаты точки d: 6, 7, 5
а > с истинно
а < b истинно
Точки and находятся на одном расстоянии от начала отсчета
На перегрузку операторов отношения накладывается следующее важное ограничение: они должны перегружаться попарно. Так, если перегружается оператор <, то следует перегрузить и оператор >, и наоборот. Ниже приведены составленные в пары перегружаемые операторы отношения.
== !=
< >
<= >=
И еще одно замечание: если перегружаются операторы == и !=, то для этого обычно требуется также переопределить методы Object.Equals()
и Object.GetHashCode()
. Эти методы и способы их переопределения подробнее рассматриваются в главе 11.
Ключевые слова true
и false
можно также использовать в качестве унарных операторов для целей перегрузки. Перегружаемые варианты этих операторов позволяют определить назначение ключевых слов true
и false
специально для создаваемых классов. После перегрузки этих ключевых слов в качестве унарных операторов для конкретного класса появляется возможность использовать объекты этого класса для управления операторами if, while, for и do-while
или же в условном выражении ?
.
Операторы true
и false
должны перегружаться попарно, а не раздельно. Ниже приведена общая форма перегрузки этих унарных операторов.
public static bool operator true(тип_параметра операнд)
{
// Возврат логического значения true или false.
}
public static bool operator false(тип_параметра операнд)
{
// Возврат логического значения true или false.
}
Обратите внимание на то, что и в том и в другом случае возвращается результат типа bool
.
Ниже приведен пример программы, демонстрирующий реализацию операторов true и false
в классе ThreeD
. В каждом из этих операторов проверяется следующее условие: если хотя бы одна из координат объекта типа ThreeD
равна нулю, то этот объект истинен, а если все три его координаты равны нулю, то такой объект ложен. В данном примере программы реализован также оператор декремента исключительно в целях демонстрации.
// Перегрyзить операторы true и false для класса ThreeD.
using System;
// Класс для xранения треxмерныx координат,
class ThreeD {
int x, y, z; // треxмерные координаты
public ThreeD() { x = y = z = 0; }
public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }
// Перегрyзить оператор true.
public static bool operator true(ThreeD op) {
if ((op.x != 0) || (op.y != 0) || (op.z != 0))
return true; // xотя бы одна координата не равна нyлю
else
return false;
}
// Перегрyзить оператор false.
public static bool operator false(ThreeD op) {
if ((op.x == 0) && (op.y == 0) && (op.z == 0))
return true; // все координаты равны нyлю
else
return false;
}
// Перегрyзить yнарный оператор –.
public static ThreeD operator –(ThreeD op) {
ThreeD result = new ThreeD();
// Возвратить резyльтат декрементирования,
result.x = op.x – 1;
result.y = op.y – 1;
result.z = op.z – 1;
return result;
}
// Вывести координаты X, Y, Z. •
public void Show() {
Console.WriteLine(x + ", " + y + ", " + z);
}
}
class TrueFalseDemo {
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.Show();
Console.WriteLine();
if (a) Console.WriteLine(«Точка а истинна.»);
else Console.WriteLine(«Точка а ложна.»);
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);
}
}
Выполнение этой программы приводит к следующему результату.
Координаты точки а: 5, 6, 7
Координаты точки b: 10, 10, 10
Координаты точки с: 0, 0, 0
Точка а истинна
Точка b истинна
Точка с ложна
Управление циклом с помощью объекта класса 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
Обратите внимание на то, как объекты класса ThreeD
используются для управления условным оператором if
и оператором цикла do-while
. Так, в операторах if
объект типа ThreeD
проверяется с помощью оператора true
. Если результат этой проверки оказывается истинным, то оператор if
выполняется. А в операторе цикла do-while
объект b декрементируется на каждом шаге цикла. Следовательно, цикл повторяется до тех пор, пока проверка объекта b дает истинный результат, т.е. этот объект содержит хотя бы одну ненулевую координату. Если же окажется, что объект b содержит все нулевые координаты, его проверка с помощью оператора true
даст ложный результат и цикл завершится.
Как вам должно быть уже известно, в C# предусмотрены следующие логические операторы: &, |, !, && и ||.
Из них перегрузке, безусловно, подлежат только операторы &, |
и !
. Тем не менее, соблюдая определенные правила, можно извлечь также пользу из укороченных логических операторов &&
и ||
. Все эти возможности рассматриваются ниже.
Простой способ перегрузки логических операторов
Рассмотрим сначала простейший случай. Если не пользоваться укороченными логическими операторами, то перегрузку операторов & и | можно выполнять совершенно естественным путем, получая в каждом случае результат типа bool
. Аналогичный результат, как правило, дает и перегружаемый оператор !.
Ниже приведен пример программы, в которой демонстрируется перегрузка логических операторов !, & и | для объектов типа ThreeD. Как и в предыдущем примере, объект типа ThreeD считается истинным, если хотя бы одна из его координат не равна нулю. Если же все три координаты объекта равны нулю, то он считается ложным.
// Простой способ перегрyзки логическиx операторов
// !, | и & для объектов класса ThreeD.
using System;
// Класс для xранения треxмерныx координат.
class ThreeD {
int x, y, z; // треxмерные координаты
public ThreeD() { x = y = z = 0; }
public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }
// Перегрyзить логический оператор |.
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;
}
// Перегрyзить логический оператор &.
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;
}
// Перегрyзить логический оператор !.
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 {
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.Show();
Console.WriteLine();
if (!a) Console.WriteLine(«Точка а ложна.»);
if (!b) Console.WriteLine(«Точка b ложна.»);
if (!c) 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 | с ложно.»);
}
}
При выполнении этой программы получается следующий результат.
Координаты точки а: 5, 6, 1
Координаты точки b: 10, 10, 10
Координаты точки с: 0, 0, 0
Точка с ложна.
а & b истинно.
а & с ложно.
а | b истинно,
а | с истинно.
При таком способе перегрузки логических операторов &, | и ! методы каждого из них возвращают результат типа bool
. Это необходимо для того, чтобы использовать рассматриваемые операторы обычным образом, т.е. в тех выражениях, где предполагается результат типа bool
. Напомним, что для всех встроенных в C# типов данных результатом логической операции должно быть значение типа bool
. Поэтому вполне разумно предусмотреть возврат значения типа bool
и в перегружаемых вариантах этих логических операторов. Но, к сожалению, такой способ перегрузки пригоден лишь в том случае, если не требуются укороченные логические операторы.
Как сделать укороченные логические операторы доступными для применения
Для того чтобы применение укороченных логических операторов &&
и ||
стало возможным, необходимо соблюсти следующие четыре правила. Во-первых, в классе должна быть произведена перегрузка логических операторов &
и |
. Во-вторых, перегружаемые методы операторов &
и |
должны возвращать значение того же типа, что и у класса, для которого эти операторы перегружаются. В-третьих, каждый параметр должен содержать ссылку на объект того класса, для которого перегружается логический оператор. И в-четвертых, для класса должны быть перегружены операторы true
и false
. Если все эти условия выполняются, то укороченные логические операторы автоматически становятся пригодными для применения.
В приведенном ниже примере программы показано, как правильно реализовать логические операторы &
и |
в классе ThreeD
, чтобы сделать доступными для применения укороченные логические операторы && и ||
.
/* Более совершенный способ перегрyзки логическиx оперaторов !, | и & для объектов клaссa ThreeD.
В этом вaриaнте yкороченные логические оперaторы && и || стaновятся достyпными для применения aвтомaтически. */
using System;
// Клaсс для xрaнения треxмерныx координaт,
class ThreeD {
int x, y, z; // треxмерные координaты
public ThreeD() { x = y = z = 0; }
public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }
// Перегрyзить логический оперaтор | для yкороченного вычисления,
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);
}
// Перегрyзить логический оперaтор & для yкороченного вычисления,
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);
}
// Перегрyзить логический оперaтор !.
public static bool operator !(ThreeD op) {
if (op)
return false;
else
return true;
}
// Перегрyзить оперaтор true.
public static bool operator true(ThreeD op) {
if ((op.x != 0) || (op.y != 0) || (op.z != 0))
return true; //xотя бы однa координaтa не рaвнa нyлю
else
return false;
}
// Перегрyзить оперaтор false.
public static bool operator false(ThreeD op) {
if ((op.x == 0) && (op.y == 0) && (op.z == 0))
return true; //все координaты рaвны нyлю
else
return false;
}
// Ввести координaты X, Y, Z.
public void Show() {
Console.WriteLine(x + ", " + y + ", " + z);
}
}
class TrueFalseDemo {
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: ");
a.Show();
Console.Write("Координaты точки b: ");
b.Show();
Console.Write("Координaты точки с: ");
c.Show();
Console.WriteLine();
if (a) Console.WriteLine(«Точкa a истиннa.»);
if (b) Console.WriteLine(«Точкa b истиннa.»);
if (c) Console.WriteLine(«Точкa с истиннa.»);
if (!a) Console.WriteLine(«Точкa a ложнa.»);
if (!b) Console.WriteLine(«Точкa b ложнa.»);
if (!c) Console.WriteLine(«Точкa с ложнa.»);
Console.WriteLine();
Console.WriteLine(«Применение логическиx оперaторов & и |»);
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();
//a теперь применить yкороченные логические оперaторы.
Console.WriteLine(«Применение yкороченныx» +
«логическиx оперaторов && и ||»);
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 || с ложно.»);
}
}
Выполнение этой программы приводит к следующему результату.
Координаты точки а: 5, 6, 7
Координаты точки b: 10, 10, 10
Координаты точки с: 0, 0, 0
Точка а истинна
Точка b истинна
Точка с ложна.
Применение логических операторов & и |
а & b истинно,
а & с ложно,
а | b истинно,
а | с истинно.
Применение укороченных логических операторов && и ||
а && b истинно,
а && с ложно,
а И b истинно,
а И с истинно.
Рассмотрим более подробно, каким образом реализуются логические операторы & и |. Они представлены в следующем фрагменте кода.
// Перегрузить логический оператор | для укороченного вычисления,
public static ThreeD operator | (ThreeD op1, ThreeD op2)
{
if(((op1.x != 0) || (op1.у != 0) || (op1.z != 0)) |
((op2.x != 0) || (op2.у != 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
, у которого хотя бы одна координата не равна нулю. Если же логическая операция дает ложный результат, то соответственно создается и возвращается ложный объект. Таким образом, результатом вычисления логического выражения а & b
в следующем фрагменте кода:
if(а & b) Console.WriteLine(«а & b истинно.»);
else Console.WriteLine(«а & b ложно.»);
является объект типа ThreeD
, который в данном случае оказывается истинным. А поскольку операторы true
и false
уже определены, то созданный объект типа ThreeD
подвергается действию оператора true
и в конечном итоге возвращается результат типа bool
. В данном случае он равен true
, а следовательно, условный оператор if
успешно выполняется.
Благодаря тому что все необходимые правила соблюдены, укороченные операторы становятся доступными для применения к объектам ThreeD
. Они действуют следующим образом. Первый операнд проверяется с помощью операторного метода operator true
(для оператора ||) или же с помощью операторного метода operator false
(для оператора &&). Если удается определить результат данной операции, то соответствующий перегружаемый оператор (& или |) далее не выполняется. В противном случае перегружаемый оператор (& или | соответственно) используется для определения конечного результата. Следовательно, когда применяется укороченный логический оператор && или ||, то соответствующий логический оператор & или | вызывается лишь в том случае, если по первому операнду невозможно определить результат вычисления выражения. В качестве примера рассмотрим следующую строку кода из приведенной выше программы.
if(а || с) Console.WriteLine(«а || с истинно.»);
В этой строке кода сначала применяется оператор true к объекту а. В данном случае объект а истинен, и поэтому использовать далее операторный метод | нет необходимости. Но если переписать данную строку кода следующим образом:
if(с || a) Console.WriteLine («с || а истинно.»);
то оператор true
был бы сначала применен к объекту с, который в данном случае ложен. А это означает, что для определения истинности объекта а пришлось бы далее вызывать операторный метод |.
Описанный выше способ применения укороченных логических операторов может показаться, на первый взгляд, несколько запутанным, но если подумать, то в таком применении обнаруживается известный практический смысл. Ведь благодаря перегрузке операторов true
и false
для класса компилятор получает разрешение на применение укороченных логических операторов, не прибегая к явной их перегрузке. Это дает также возможность использовать объекты в условных выражениях. И вообще, логические операторы & и | лучше всего реализовывать полностью, если, конечно, не требуется очень узко направленная их реализация.
Операторы преобразования
Иногда объект определенного класса требуется использовать в выражении, включающем в себя данные других типов. В одних случаях для этой цели оказывается пригодной перегрузка одного или более операторов, а в других случаях – обыкновенное преобразование типа класса в целевой тип. Для подобных ситуаций в C# предусмотрена специальная разновидность операторного метода, называемая оператором преобразования.Такой оператор преобразует объект исходного класса в другой тип. Операторы преобразования помогают полностью интегрировать типы классов в среду программирования на С#, разрешая свободно пользоваться классами вместе с другими типами данных, при условии, что определен порядок преобразования в эти типы.
Существуют две формы операторов преобразования: явная и неявная. Ниже они представлены в общем виде:
public static explicit operator целевой_тип{исходный_тип v) {return значение;}
public static implicit operator целевой_тип(исходный_тип v) {return значение;}
где целевой_тип обозначает тот тип, в который выполняется преобразование; исходный_тип – тот тип, который преобразуется; значение – конкретное значение, приобретаемое классом после преобразования. Операторы преобразования возвращают данные, имеющие целевой_тип, причем указывать другие возвращаемые типы данных не разрешается.
Если оператор преобразования указан в неявной форме (implicit
), то преобразование вызывается автоматически, например, в том случае, когда объект используется в выражении вместе со значением целевого типа. Если же оператор преобразования указан в явной форме (explicit
), то преобразование вызывается в том случае, когда выполняется приведение типов. Для одних и тех же исходных и целевых типов данных нельзя указывать оператор преобразования одновременно в явной и неявной форме.
Создадим оператор преобразования специально для класса ThreeD
, чтобы продемонстрировать его применение. Допустим, что требуется преобразовать объект типа ThreeD
в целое значение, чтобы затем использовать его в целочисленном выражении. Такое преобразование требуется, в частности, для получения произведения всех трех координат объекта. С этой целью мы воспользуемся следующей неявной формой оператора преобразования.
public static implicit operator int(ThreeD op1)
{
return op1.x * op1.у * op1.z;
}
Ниже приведен пример программы, демонстрирующей применение этого оператора преобразования.
// Пример применения оператора неявного преобразования,
using System;
// Класс для хранения трехмерных координат,
class ThreeD {
int x, y, z; // трехмерные координаты
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.x = 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 {
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("Результат сложения a + b: ");
c.Show();
Console.WriteLine();
i = a; // преобразовать в тип int
Console.WriteLine("Результат присваивания i = a: " + i);
Console.WriteLine();
i = a * 2 – b; // преобразовать в тип int
Console.WriteLine("Результат вычисления выражения a * 2 – b " + i);
}
}
Вот к какому результату приводит выполнение этой программы.
Координаты точки а: 1, 2, 3
Координаты точки b: 10, 10, 10
Результат сложения а+b: 11, 12, 13
Результат присваивания i = а: 6
Результат вычисления выражения а * 2 – b: -988
Как следует из приведенного выше примера программы, когда объект типа ThreeD
используется в таком целочисленном выражении, как i = а
, происходит его преобразование. В этом конкретном случае преобразование приводит к возврату целого значения 6, которое является произведением координат точки а, хранящихся в объекте того же названия. Но если для вычисления выражения преобразование в тип int
не требуется, то оператор преобразования не вызывается. Именно поэтому операторный метод operator int()
не вызывается при вычислении выражения с = а + b
.
Но для различных целей можно создать разные операторы преобразования. Так, для преобразования объекта типа ThreeD
в тип double
можно было бы определить второй оператор преобразования. При этом каждый вид преобразования выполнялся бы автоматически и независимо от другого.
Оператор неявного преобразования применяется автоматически в следующих случаях: когда в выражении требуется преобразование типов; методу передается объект; осуществляется присваивание и производится явное приведение к целевому типу. С другой стороны, можно создать оператор явного преобразования, вызываемый только тогда, когда производится явное приведение типов. В таком случае оператор явного преобразования не вызывается автоматически. В качестве примера ниже приведен вариант предыдущей программы, переделанный для демонстрации явного преобразования в тип int
.
// Применить явное преобразование,
using System;
// Класс для хранения трехмерных координат,
class ThreeD {
int x, y, z; // трехмерные координаты
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 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 {
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("Результат сложения a + b: ");
c.Show();
Console.WriteLine();
i = (int)a; // преобразовать в тип int явно,
// поскольку указано приведение типов
Console.WriteLine("Результат присваивания i = а: " + i);
Console.WriteLine();
i = (int)a * 2 – (int)b; // явно требуется приведение типов
Console.WriteLine("Результат вычисления выражения а * 2 – b: " + i);
}
}
Оператор преобразования теперь указан в явной форме, и поэтому преобразование должно быть явно приведено к типу int
. Например, следующая строка кода не будет скомпилирована, если исключить приведение типов.
i = (int) а; // преобразовать в тип int явно,
// поскольку указано приведение типов
На операторы преобразования накладывается ряд следующих ограничений.
• Исходный или целевой тип преобразования должен относиться к классу, для которого объявлено данное преобразование. В частности, нельзя переопределить преобразование в тип int
, если оно первоначально указано как преобразование в тип double
.
• Нельзя указывать преобразование в класс object
или же из этого класса.
• Для одних и тех же исходных и целевых типов данных нельзя указывать одновременно явное и неявное преобразование.
• Нельзя указывать преобразование базового класса в производный класс. (Подробнее о базовых и производных классах речь пойдет в главе 11.)
• Нельзя указывать преобразование в интерфейс или же из него. (Подробнее об интерфейсах – в главе 12.)
Помимо указанных выше ограничений, имеется ряд рекомендаций, которыми обычно руководствуются при выборе операторов явного или неявного преобразования. Несмотря на все преимущества неявных преобразований, к ним следует прибегать только в тех случаях, когда преобразованию не свойственны ошибки. Во избежание подобных ошибок неявные преобразования должны быть организованы только в том случае, если удовлетворяются следующие условия. Во-первых, информация не теряется, например, в результате усечения, переполнения или потери знака. И во-вторых, преобразование не приводит к исключительной ситуации. Если же неявное преобразование не удовлетворяет этим двум условиям, то следует выбрать явное преобразование.