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


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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



Как вам должно быть уже известно, в С# предусмотрены следующие логические операторы: &, |,!, && и ||. Из них перегрузке, безусловно, подлежат только опера­торы &, | и!. Тем не менее, соблюдая определенные правила, можно извлечь также пользу из укороченных логических операторов && и ||. Все эти возможности рассма­триваются ниже.

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

Рассмотрим сначала простейший случай. Если не пользоваться укороченными ло­гическими операторами, то перегрузку операторов & и | можно выполнять совершен­но естественным путем, получая в каждом случае результат типа bool. Аналогичный результат, как правило, дает и перегружаемый оператор!.

Ниже приведен пример программы, в которой демонстрируется перегрузка логи­ческих операторов!, & и | для объектов типа ThreeD. Как и в предыдущем примере, объект типа ThreeD считается истинным, если хотя бы одна из его координат не равна нулю. Если же все три координаты объекта равны нулю, то он считается ложным.

Листинг 9.10

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

//!, |, и & для объектов класса ThreeD.

 

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(((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)

{

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

{

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.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("Точка c ложна.");

 

Console.WriteLine();

 

if(a & b) Console.WriteLine("a & b истинно.");

else Console.WriteLine("a & b ложно.");

 

if(a & c) Console.WriteLine("a & c истинно.");

else Console.WriteLine("a & c ложно.");

 

if(a | b) Console.WriteLine("a | b истинно.");

else Console.WriteLine("a | b ложно.");

 

if(a | c) Console.WriteLine("a | c истинно.");

else Console.WriteLine("a | c ложно.");

}

}

При выполнении этой программы получается следующий результат.

Координаты точки а: 5, 6, 7

Координаты точки Ь: 10, 10, 10

Координаты точки с: 0, 0, 0

 

Точка с ложна.

 

а & b истинно.

а & с ложно.

а | b истинно.

а | с истинно.

При таком способе перегрузки логических операторов &, | и! методы каждого из них возвращают результат типа bool. Это необходимо для того, чтобы использовать рассматриваемые операторы обычным образом, т.е. в тех выражениях, где предпола­гается результат типа bool. Напомним, что для всех встроенных в С# типов данных результатом логической операции должно быть значение типа bool. Поэтому впол­не разумно предусмотреть возврат значения типа bool и в перегружаемых вариан­тах этих логических операторов. Но, к сожалению, такой способ перегрузки пригоден лишь в том случае, если не требуются укороченные логические операторы.

 

Как сделать укороченные логические операторы доступными

Для применения

Для того чтобы применение укороченных логических операторов && и || стало возможным, необходимо соблюсти следующие четыре правила. Во-первых, в классе должна быть произведена перегрузка логических операторов & и |. Во-вторых, пере­гружаемые методы операторов & и | должны возвращать значение того же типа, что и у класса, для которого эти операторы перегружаются. В-третьих, каждый параметр должен содержать ссылку на объект того класса, для которого перегружается логиче­ский оператор. И в-четвертых, для класса должны быть перегружены операторы true и false. Если все эти условия выполняются, то укороченные логические операторы автоматически становятся пригодными для применения.

В приведенном ниже примере программы показано, как правильно реализовать логические операторы & и | в классе ThreeD, чтобы сделать доступными для примене­ния укороченные логические операторы && и ||.

Листинг 9.11

/* Более совершенный способ перегрузки логических

операторов!, | и & для объектов класса ThreeD.

В этом варианте укороченные логические операторы && и ||

становятся доступными для применения автоматически. */

 

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)

{

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);

}

 

// Перегрузить логический оператор!.

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

{

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("Точка c истинна.");

 

if(!a) Console.WriteLine("Точка a ложна.");

if(!b) Console.WriteLine("Точка b ложна.");

if(!c) Console.WriteLine("Точка c ложна.");

 

Console.WriteLine();

 

Console.WriteLine("Применение логических операторов & и |");

if(a & b) Console.WriteLine("a & b истинно.");

else Console.WriteLine("a & b ложно.");

 

if(a & c) Console.WriteLine("a & c истинно.");

else Console.WriteLine("a & c ложно.");

 

if(a | b) Console.WriteLine("a | b истинно.");

else Console.WriteLine("a | b ложно.");

 

if(a | c) Console.WriteLine("a | c истинно.");

else Console.WriteLine("a | c ложно.");

 

Console.WriteLine();

 

// А теперь применить укороченные логические операторы.

Console.WriteLine("Применение укороченных" +

"логических операторов && и ||");

if(a && b) Console.WriteLine("a && b истинно.");

else Console.WriteLine("a && b ложно.");

 

if(a && c) Console.WriteLine("a && c истинно.");

else Console.WriteLine("a && c ложно.");

 

if(a || b) Console.WriteLine("a || b истинно.");

else Console.WriteLine("a || b ложно.");

 

if(a || c) Console.WriteLine("a || c истинно.");

else Console.WriteLine("a || c ложно.");

}

}

Выполнение этой программы приводит к следующему результату.

Координаты точки а: 5, 6, 7

Координаты точки b: 10, 10, 10

Координаты точки с: 0, 0, 0

 

Точка а истинна.

Точка b истинна.

Точка с ложна.

 

Применение логических операторов & и |

а & b истинно.

а & с ложно.

а | b истинно.

а | с истинно.

 

Применение укороченных логических операторов && и ||

а && b истинно.

а && с ложно.

а || b истинно.

а || с истинно.

Рассмотрим более подробно, каким образом реализуются логические операторы & и |. Они представлены в следующем фрагменте кода.

// Перегрузить логический оператор | для укороченного вычисления.

public static ThreeD operator | (ThreeD op1, ThreeD op2)

{

if(((opl.x!= 0) || (opl.у!= 0) || (opl.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 opl, ThreeD op2)

{

if(((opl.x!= 0) && (opl.у!= 0) && (opl.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(a || с) Console.WriteLine("а || с истинно.");

В этой строке кода сначала применяется оператор true к объекту а. В данном слу­чае объект а истинен, и поэтому использовать далее операторный метод | нет необхо­димости. Но если переписать данную строку кода следующим образом:

if(c || a) Console.WriteLine ("с || а истинно.");

то оператор true был бы сначала применен к объекту с, который в данном случае ложен. А это означает, что для определения истинности объекта а пришлось бы далее вызывать операторный метод |.

Описанный выше способ применения укороченных логических операторов может показаться, на первый взгляд, несколько запутанным, но если подумать, то в таком применении обнаруживается известный практический смысл. Ведь благодаря пере­грузке операторов true и false для класса компилятор получает разрешение на применение укороченных логических операторов, не прибегая к явной их перегрузке. Это дает также возможность использовать объекты в условных выражениях. И вообще, логические операторы & и | лучше всего реализовывать полностью, если, конечно, не требуется очень узко направленная их реализация.

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

Иногда объект определенного класса требуется использовать в выражении, вклю­чающем в себя данные других типов. В одних случаях для этой цели оказывается пригодной перегрузка одного или более операторов, а в других случаях - обыкно­венное преобразование типа класса в целевой тип. Для подобных ситуаций в С# пред­усмотрена специальная разновидность операторного метода, называемая оператором преобразования. Такой оператор преобразует объект исходного класса в другой тип. Операторы преобразования помогают полностью интегрировать типы классов в среду программирования на С#, разрешая свободно пользоваться классами вместе с другими типами данных, при условии, что определен порядок преобразования в эти типы.

Существуют две формы операторов преобразования: явная и неявная. Ниже они представлены в общем виде:

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;

}

Ниже приведен пример программы, демонстрирующей применение этого опера­тора преобразования.

Листинг 9.12

// Пример применения оператора неявного преобразования.

 

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;

}

 

// Неявное преобразование объекта типа 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; // сложить координаты точек a и b

Console.Write("Result of 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.

Листинг 9.13

// Применить явное преобразование.

 

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; // сложить координаты a и b

Console.Write("Результат сложения a + b: ");

c.Show();

Console.WriteLine();

 

i = (int) a; // преобразовать в тип int явно,

// поскольку указано приведение типов

Console.WriteLine("Результат присваивания i = a: " + i);

Console.WriteLine();

 

i = (int)a * 2 - (int)b; // явно требуется приведение типов

Console.WriteLine("Результат вычисления выражения a*2 - b: " + i);

}

}

Оператор преобразования теперь указан в явной форме, и поэтому преобразова­ние должно быть явно приведено к типу int. Например, следующая строка кода не будет скомпилирована, если исключить приведение типов.

i = (int) а; // преобразовать в тип int явно,

// поскольку указано приведение типов

На операторы преобразования накладывается ряд следующих ограничений.

- Исходный или целевой тип преобразования должен относиться к классу, для ко­торого объявлено данное преобразование. В частности, нельзя переопределить преобразование в тип int, если оно первоначально указано как преобразование в тип double.

- Нельзя указывать преобразование в класс object или же из этого класса.

- Для одних и тех же исходных и целевых типов данных нельзя указывать одно­временно явное и неявное преобразование.

- Нельзя указывать преобразование базового класса в производный класс.

- Нельзя указывать преобразование в интерфейс или же из него.

Помимо указанных выше ограничений, имеется ряд рекомендаций, которыми обычно руководствуются при выборе операторов явного или неявного преобразова­ния. Несмотря на все преимущества неявных преобразований, к ним следует прибе­гать только в тех случаях, когда преобразованию не свойственны ошибки. Во избежа­ние подобных ошибок неявные преобразования должны быть организованы только в том случае, если удовлетворяются следующие условия. Во-первых, информация не теряется, например, в результате усечения, переполнения или потери знака. И во-вторых, преобразование не приводит к исключительной ситуации. Если же неявное преобразование не удовлетворяет этим двум условиям, то следует выбрать явное пре­образование.

Рекомендации и ограничения по перегрузке операторов

Действие перегружаемого оператора распространяется на класс, для которого он определяется, и никак не связано с его первоначальным применением к данным встро­енных в С# типов. Но ради сохранения ясности структуры и удобочитаемости исходно­го кода перегружаемый оператор должен, по возможности, отражать основную суть своего первоначального назначения. Например, назначение оператора + для класса ThreeD по сути не должно заметно отличаться от его назначения для целочисленных типов данных. Если бы, например, определить оператор + относительно некоторого класса таким образом, чтобы по своему действию он стал больше похожим на опера­тор /, то вряд ли от этого было бы много проку. Главный принцип перегрузки опера­торов заключается в следующем: несмотря на то, что перегружаемый оператор может получить любое назначение, ради ясности новое его назначение должно быть так или иначе связано с его первоначальным назначением.

На перегрузку операторов накладывается ряд ограничений. В частности, нельзя из­менять приоритет любого оператора или количество операндов, которое требуется для оператора, хотя в операторном методе можно и проигнорировать операнд. Кроме того, имеется ряд операторов, которые нельзя перегружать. А самое главное, что пере­грузке не подлежит ни один из операторов присваивания, в том числе и составные, как, например, оператор +=. Ниже перечислены операторы, которые нельзя перегру­жать. Среди них имеются и такие операторы, которые будут рассматриваться далее.

&& ().?

?? [] || =

=> -> as checked

default is new sizeof

typeof unchecked

Несмотря на то что оператор приведения () нельзя перегружать явным образом, имеется все же возможность создать упоминавшиеся ранее операторы преобразова­ния, выполняющие ту же самую функцию.

Ограничение, связанное с тем, что некоторые операторы, например +=, нельзя перегружать, на самом деле не является таким уж непреодолимым. Вообще говоря, если оператор определен как перегружаемый и используется в составном операторе присваивания, то обычно вызывается метод этого перегружаемого оператора. Следо­вательно, при обращении к оператору += в программе автоматически вызывается за­ранее объявленный вариант метода opera tor+(). Например, в приведенном ниже фрагменте кода метод operator+() автоматически вызывается для класса ThreeD, а в итоге объект b будет содержать координаты 11,12,13.

ThreeD а = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

 

b += a; // сложить координаты точек а и b

И последнее замечание: несмотря на то, что оператор индексации массива [] нель­зя перегружать с помощью операторного метода, имеется возможность создать индек­саторы, о которых речь пойдет в следующей главе.

 

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

Во всех предыдущих примерах программ, представленных в этой главе, для демон­страции перегрузки операторов использовался класс ThreeD, и этой цели он служил исправно. Но прежде чем завершить эту главу, было бы уместно рассмотреть еще один пример перегрузки операторов. Общие принципы перегрузки операторов остаются неизменными независимо от применяемого класса, тем не менее, в рассматриваемом ниже примере наглядно демонстрируются сильные стороны такой перегрузки, осо­бенно если это касается расширяемости типов.

В данном примере разрабатывается 4-разрядный целочисленный тип данных и для него определяется ряд операций. Вам, вероятно, известно, что на ранней стадии развития вычислительной техники широко применялся тип данных для обозначения 4-разрядных двоичных величин, называвшихся полубайтами, поскольку они составля­ли половину байта, содержали одну шестнадцатеричную цифру и были удобны для ввода кода полубайтами с пульта ЭВМ, что в те времена считалось привычным заня­тием для программистов! В наше время этот тип данных применяется редко, но он по-прежнему является любопытным дополнением целочисленных типов данных в С#. По традиции полубайт обозначает целое значение без знака.

В приведенном ниже примере программы тип полубайтовых данных реализуется с помощью класса Nybble. В качестве базового для него используется тип int, но с огра­ничением на хранение данных от 0 до 15. В классе Nybble определяются следующие операторы.

- Сложение двух объектов типа Nybble.

- Сложение значения типа int с объектом типа Nybble.

- Сложение объекта типа Nybble со значением типа int.

- Операции сравнения: больше (>) и меньше (<).

- Операция инкремента.

- Преобразование значения типа int в объект типа Nybble.

- Преобразование объекта типа Nybble в значение типа int.

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

Ниже полностью приводится класс Nybble, а также класс NybbleDemo, демонстри­рующий его применение.

Листинг 9.14

// Создать полубайтовый тип 4-разрядных данных под названием Nybble.

 

using System;

 

// Тип 4-разрядных данных.

class Nybble

{

int val; // базовый тип для хранения данных

 

public Nybble() { val = 0; }

 

public Nybble(int i)

{

val = i;

val = val & 0xF; // сохранить 4 младших разряда

}

 

// Перегрузить бинарный оператор +

// для сложения двух объектов типа 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;

}

 

// Перегрузить бинарный оператор + для сложения

// значения типа 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)

{

Nybble result = new Nybble();

result.val = op.val + 1;

 

result.val = result.val & 0xF; // сохранить 4 младших разряда

return result;

}

 

// Перегрузить оператор >.

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

{

static void Main()

{

Nybble a = new Nybble(1);

Nybble b = new Nybble(10);

Nybble c = new Nybble();

int t;

 

Console.WriteLine("a: " + (int) a);

Console.WriteLine("b: " + (int) b);

 

// Использовать тип Nybble в условном операторе if.

if(a < b) Console.WriteLine("a меньше b\n");

 

// Сложить два объекта типа Nybble.

c = a + b;

Console.WriteLine("c после операции c = a + b: " + (int) c);

 

// Сложить значение типа int с объектом типа Nybble.

a += 5;

Console.WriteLine("a после операции a += 5: " + (int) a);

 

Console.WriteLine();

 

// Использовать тип Nybble в выражении типа.

t = a * 2 + 3;

Console.WriteLine("Результат вычисления выражения a*2 + 3: " + t);

 

Console.WriteLine();

 

// Продемонстрировать присваивание значения типа int

// и переполнение.

a = 19;

Console.WriteLine("Результат присваивания a = 19: " + (int) a);

 

Console.WriteLine();

 

// Использовать тип Nybble для управления циклом.

Console.WriteLine("Управление циклом for " +

"с помощью объекта типа Nybble.");

for(a = 0; a < 10; a++)

Console.Write((int) a + " ");

 

Console.WriteLine();

}

}

При выполнении этой программы получается следующий результат.

 

а: 1

b: 10

а меньше b

 

с после операции с = а + b: 11

а после операции а += 5: 6

 

Результат вычисления выражения а * 2 + 3: 15

 

Результат присваивания а = 19: 3

 

Управление циклом for с помощью объекта типа Nybble.

0 1 2 3 4 5 6 7 8 9

Большая часть функций класса Nybble не требует особых пояснений. Тем не менее необходимо подчеркнуть ту особую роль, которую операторы преобразования игра­ют в интегрировании класса типа Nybble в систему типов С#. В частности, объект типа Nybble можно свободно комбинировать с данными других типов в арифметических вы­ражениях, поскольку определены преобразования объекта этого типа в тип int и обрат­но. Рассмотрим для примера следующую строку кода из приведенной выше программы.

t = а * 2 + 3;

В этом выражении переменная t и значения 2 и 3 относятся к типу int, но в ней присутствует также объект типа Nybble. Оба типа оказываются совместимыми благо­даря неявному преобразованию типа Nybble в тип int. В данном случае остальная часть выражения относится к типу int, поэтому объект а преобразуется в тип int с помощью своего метода преобразования.

А благодаря преобразованию типа int в тип Nybble значение типа int может быть присвоено объекту типа Nybble. Например, в следующей строке из приведенной выше программы:

а = 19;

сначала выполняется оператор преобразования типа int в тип Nybble. Затем созда­ется новый объект типа Nybble, в котором сохраняются 4 младших разряда целого значения 19, а по существу, число 3, поскольку значение 19 превышает диапазон пред­ставления чисел для типа Nybble. Далее этот объект присваивается переменной эк­земпляра а. Без операторов преобразования подобные выражения были бы просто недопустимы.

Кроме того, преобразование типа Nybble в тип Nybble используется в цикле for. Без такого преобразования организовать столь простой цикл for было бы просто не­возможно.

В качестве упражнения попробуйте создать вариант полубайтового типа Nybble, предотвращающий переполнение, если присваиваемое значение оказывается за пределами допустимого диапазона чисел. Для этой цели лучше всего сгенерировать исключение.

 



Поделиться:


Последнее изменение этой страницы: 2016-12-30; просмотров: 187; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.117.196.184 (1.133 с.)