Шаг 118.
Язык программирования C#. Начала
Перегрузка операторов. Примеры использования
На этом шаге мы рассмотрим несколько примеров решения задач, в которых используется перегрузка операторов.
Здесь мы рассмотрим несколько программ, в которых используются ранее рассмотренные способы перегрезки операторов.
Задание 1. Напишите программу, в которой есть класс с целочисленным полем и текстовым полем. Выполните перегрузку всех операторов сравнения. Сравнение на предмет "больше"
или "меньше" выполняется на основе сравнения длины текстовых значений (имеются в виду текстовые поля сравниваемых объектов). При сравнении на предмет "больше или равно" или "меньше или
равно" сравниваются значения целочисленных полей объектов. При сравнении на предмет "равно" или "не равно" сравниваются и целочисленные, и текстовые поля объектов. Также предложите способ
переопределения методов Equals() и GetHashCode().
Раскрыть/скрыть решение и комментарии.
Прежде всего приведем текст программы.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr118_1
{
// Класс с перегрузкой операторов сравнения == и !=
// и переопределением методов GetHashCode() и Equals():
class MyClass
{
// Целочисленное поле:
public int code;
// Текстововое поле:
public string txt;
// Конструктор с двумя аргументами:
public MyClass(int n, string t)
{
code = n; // Значение целочисленного поля
txt = t; // Значение текстового поля
}
// Перегрузка оператора "меньше":
public static bool operator <(MyClass a, MyClass b)
{
return a.txt.Length < b.txt.Length;
}
// Перегрузка оператора "больше":
public static bool operator >(MyClass a, MyClass b)
{
return a.txt.Length > b.txt.Length;
}
// Перегрузка оператора "меньше или равно":
public static bool operator <=(MyClass a, MyClass b)
{
if (a.code <= b.code) return true;
else return false;
}
// Перегрузка оператора "больше или равно":
public static bool operator >=(MyClass a, MyClass b)
{
if (a.code >= b.code) return true;
else return false;
}
// Переопределение метода GetHashCode():
public override int GetHashCode()
{
// Вычисление хэш-кода:
return code ^ txt[0];
}
// Переопределение метода Equals():
public override bool Equals(Object obj)
{
// Локальная объектная переменная:
MyClass t = (MyClass)obj;
// Результат метода:
if (code == t.code && txt == t.txt)
return true;
else return false;
}
// Перегрузка оператора "равно":
public static bool operator ==(MyClass a, MyClass b)
{
// Вызов метода Equals():
return a.Equals(b);
}
// Перегрузка оператора "не равно":
public static bool operator !=(MyClass a, MyClass b)
{
// Использование оператора "равно":
return !(a == b);
}
}
// Класс с главным методом:
class Program
{
// Главный метод:
static void Main()
{
// Создание объектов:
MyClass A = new MyClass(100, "A");
MyClass B = new MyClass(100, "B");
MyClass C = new MyClass(200, "A");
MyClass D = new MyClass(100, "Aaa");
MyClass E = new MyClass(100, "A");
// Использование операторов строгого сравнения:
Console.WriteLine("A<D дает {0}", A < D);
Console.WriteLine("A>D дает {0}", A > D);
Console.WriteLine("A<С дает {0}", A < C);
Console.WriteLine("A>С дает {0}", A > C);
// Использование операторов нестрогого сравнения:
Console.WriteLine("А>=C дает {0}", A >= C);
Console.WriteLine("А<=C дает {0}", A <= B);
Console.WriteLine("B>=D дает {0}", B >= D);
Console.WriteLine("B<=D дает {0}", B <= D);
// Сравнение объектов на предмет
// равенства и неравенства:
Console.WriteLine("А==В дает {0}", A == B);
Console.WriteLine("А!=В дает {0}", A != B);
Console.WriteLine("А==С дает {0}", A == C);
Console.WriteLine("А!=С дает {0}", A != C);
Console.WriteLine("A==D дает {0}", A == D);
Console.WriteLine("A!=D дает {0}", A != D);
Console.WriteLine("А==E дает {0}", A == E);
Console.WriteLine("А!=E дает {0}", A != E);
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Реализация приложения в целом не представляет сложности, так как в условии задачи написано, как реализовать ту или иную перегрузку. Единственное затруднение может вызывать реализация методов
Equals() и GetHashCode().
Начнем с последнего метода. Напомним, что хэш-коды используются для сравнения двух объектов на предмет равенства. Главное правило такое: если два объекта считаются одинаковыми (равными), то у них должны быть одинаковые хэш-коды.
Метод GetHashCode() в нашей программе нигде не участвует, поэтому его реализация особо ни на что не повлияет. Мы его реализовали следующим образом:
// Вычисление хэш-кода:
return code ^ txt[0];
Это метод возвращает целое число. Результат вычисляется как значение выражения code^symb. Здесь использован оператор "побитового исключающего или" ^, а операндами являются поля code и
txt[0] (для символа берется код из кодовой таблицы) объекта, из которого вызывается метод.
Метод Equals() переопределяется так. В теле метода объявляется локальная объектная переменная t класса МуСlass. Значение переменной присваивается командой
// Локальная объектная переменная:
MyClass t = (MyClass)obj;
где через
obj обозначен аргумент метода
Equals(). В этой команде выполняется явное приведение переменной
obj класса
Object к ссылке класса
MyClass. Это означает,
что мы собираемся обрабатывать аргумент метода как объектную переменную класса
MyClass.
Результат метода вычисляется с помощью условной конструкции. Если совпадают значения полей объекта, из которого вызывается метод, и объекта, переданного аргументом методу, то метод Equals() результатом возвращает значение
true, а в противном случае результат метода равен false.
// Результат метода:
if (code == t.code && txt == t.txt)
return true;
else return false;
Этот метод используется при реализации перегрузки оператора ==.
Результат работы приложения изображен на рисунке 1.
Рис.1. Результат работы приложения
Задание 2. Напишите программу, в которой есть класс с целочисленным полем. Перегрузите операторы
&, |, true и
false так, чтобы с объектами класса можно
было использовать операторы
&& и
||. Перегрузку следует реализовать так, чтобы объект считался "истинным", если значение его числового поля равно 2, 3, 5 или 7. Объект должен рассматриваться
как "ложный", если значение его числового поля меньше 1 или больше 10.
Раскрыть/скрыть решение и комментарии.
Вот программа, решающая указанную задачу:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr118_2
{
// Класс с перегрузкой операторов:
class MyClass
{
// Целочисленное поле:
public int code;
// Конструктор с одним аргументом:
public MyClass(int n)
{
code = n; // Значение поля
}
// Перегрузка оператора true:
public static bool operator true(MyClass obj)
{
switch (obj.code)
{
case 2:
case 3:
case 5:
case 7:
return true;
default:
return false;
}
}
// Перегрузка оператора false:
public static bool operator false(MyClass obj)
{
if (obj) return obj.code < 1 || obj.code > 10;
else return true;
}
// Перегрузка оператора &:
public static MyClass operator &(MyClass a, MyClass b)
{
if (a) return b; else return a;
}
// Перегрузка оператора |
public static MyClass operator |(MyClass a, MyClass b)
{
if (a) return a; else return b;
}
}
// Класс с главным методом:
class Program
{
// Главный метод:
static void Main()
{
// Создание объектоз:
MyClass A = new MyClass(2);
MyClass B = new MyClass(6);
MyClass E = new MyClass(5);
MyClass Y = new MyClass(12);
// Использование логических операторов:
Console.WriteLine("Выражение A&&B: {0}", (A && B).code);
Console.WriteLine("Выражение B&&A: {0}", (B && A).code);
Console.WriteLine("Выражение A&&E: {0}", (A && E).code);
Console.WriteLine("Выражение E&&A: {0}", (E && A).code);
Console.WriteLine("Выражение A&&Y: {0}", (A && Y).code);
Console.WriteLine("Выражение Y&&A: {0}", (Y && A).code);
Console.WriteLine("Выражение A||B: {0}", (A || B).code);
Console.WriteLine("Выражение В||A: {0}", (B || A).code);
Console.WriteLine("Выражение A||E: {0}", (A || E).code);
Console.WriteLine("Выражение E||A: {0}", (E || A).code);
Console.WriteLine("Выражение А||Y: {0}", (A || Y).code);
Console.WriteLine("Выражение Y||A: {0}", (Y || A).code);
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Прежде всего реализуем перегрузку операторов true и false. По условию задачи сказано, что объект считается истинным, если значение его числового поля равно 2, 3, 5 или 7.
Это реализовано так:
switch (obj.code)
{
case 2:
case 3:
case 5:
case 7:
return true;
default:
return false;
}
Более интересно реализован оператор false. По условию сказано, что объект должен рассматриваться как "ложный", если значение его числового поля меньше 1 или больше 10. Таким образом,
будем считать объект "ложным", если он не "истинный" или его числовое поле удовлетворяет указанному условию:
if (obj) return obj.code < 1 || obj.code > 10;
else return true;
Реализацию операторов & и | мы взяли из примера 114 шага:
операторный метод operator&() описан так, что если первый аргумент (объект а класса MyClass) "истинный", то результатом возвращается ссылка на второй аргумент (объект b класса MyClass).
Если первый операнд не "истинный", то результатом возвращается ссылка на него.
Операторный метод operator|() возвращает ссылку на первый операнд, если он "истинный". Если первый операнд не "истинный", то результатом метода возвращается ссылка на второй операнд.
Результат работы приложения изображен на рисунке 2.
Рис.2. Результат работы приложения
Задание 3. Напишите программу, и которой есть класс с текстовым полем. Опишите в классе операторные методы для выполнения приведения типов. Необходимо определить
следующие способы преобразований. При преобразовании объекта в целое число результатом возвращается количество символов в значении текстового поля. При преобразовании объекта в символ
результатом является первый символ в тексте. При преобразовании числа в объект создается (и возвращается результатом соответствующего операторного метода) объект, текстовое поле которого
содержит текстовую строку из символов
'А'. Количество символов в тексте определяется преобразуемым числом.
Раскрыть/скрыть решение и комментарии.
Программа, решающая эту задачу, приведена ниже:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr118_3
{
// Класс с перегрузкой операторов приведения типа:
class MyClass
{
public String text; // Текстовое поле
// Конструктор с тремя аргументами:
public MyClass(String t)
{
text = t; // Значение текстового поля
}
// Переопределение метода ToString():
public override String ToString()
{
// Локальная текстовая строка:
String txt = "Teкстовое поле: \"" + text + "\"";
// Результат метода:
return txt;
}
// Метод для явного приведения к типу int:
public static explicit operator int(MyClass obj)
{
return obj.text.Length;
}
// Метод для явного приведения к типу char:
public static explicit operator char(MyClass obj)
{
return obj.text[0];
}
// Метод для явного преобразования из типа int:
public static explicit operator MyClass(int n)
{
String s = "";
for (int i = 0; i < n; i++)
s += "A";
MyClass t = new MyClass(s);
return t;
}
}
// Класс с главным методом:
class Program
{
// Главный метод:
static void Main()
{
// Создание объектов и проверка результата.
// Явное создание объекта:
MyClass A = new MyClass("Alpha");
// Неявно вызывается метод ToString():
Console.WriteLine("Объект А. " + A);
// Явное преобразование в тип int:
Console.WriteLine("Число: " + (int)A);
// Явное преобразование в тип char:
Console.WriteLine("Символ: " + (char)A);
// Создание объекта преобразованием из типа int:
MyClass B = (MyClass)10;
// Неявно вызывается метод ToString():
Console.WriteLine("Объект В. " + B);
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Из условия задачи не совсем понятно, какое приведение типов (явное или неявное) нужно реализовать. Для уменьшения объема программы здесь реализовано только явное приведение типов,
то есть использовано ключевое слово explicit.
Перегрузка методов приведения типов достаточно простая и понятна из условия задачи. Некоторый интерес может вызывать только метод явного преобразования из типа int:
String s = "";
for (int i = 0; i < n; i++)
s += "A";
MyClass t = new MyClass(s);
return t;
Здесь создается локальная переменная s, в которую помещается то количество символов 'A', которое совпадает со значеним переменной, переданной методу в качестве аргумента.
Затем эта переменная s выступает в качестве аргумента для конструктора класса MyClass, при выполнении которого создается объект t этого класса. Этот объект
возвращается в качестве результата работы этого метода.
Результат работы приложения изображен на рисунке 3.
Рис.3. Результат работы приложения
Со следующего шага мы начнем рассматривать свойства и индексаторы.
Предыдущий шаг
Содержание
Следующий шаг