Книга: C# 4.0: полное руководство
Перегрузка логических операторов
Разделы на этой странице:
Перегрузка логических операторов
Как вам должно быть уже известно, в 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.)
Помимо указанных выше ограничений, имеется ряд рекомендаций, которыми обычно руководствуются при выборе операторов явного или неявного преобразования. Несмотря на все преимущества неявных преобразований, к ним следует прибегать только в тех случаях, когда преобразованию не свойственны ошибки. Во избежание подобных ошибок неявные преобразования должны быть организованы только в том случае, если удовлетворяются следующие условия. Во-первых, информация не теряется, например, в результате усечения, переполнения или потери знака. И во-вторых, преобразование не приводит к исключительной ситуации. Если же неявное преобразование не удовлетворяет этим двум условиям, то следует выбрать явное преобразование.
- ГЛАВА 9 Перегрузка операторов
- 4.4. Логические элементы и синтез логических схем
- Перегрузка методов с несколькими параметрами типа
- Перегрузка конструкторов
- Практическая работа 47. Расчеты с использованием логических функций
- Использование операторов if для выбора вариантов
- Подробнее о психологических типах
- ОО-разработка и перегрузка
- Применение вложенных операторов from
- Операторы побитовых логических операций и сдвига
- Порядок выполнения операторов
- Диспетчер логических дисков