Перегрузка унарных операторов
Возьмем программу с первого примера с деньгами, и перегрузим там унарные операторы «++», «--» (добавление/вычитание 1 единицы денег):
public class Money
{
public decimal Amount { get; set; }
public string Unit { get; set; }
public Money(decimal amount, string unit)
{
Amount = amount;
Unit = unit;
}
public static Money operator +(Money a, Money b)
{
if (a.Unit != b.Unit)
throw new InvalidOperationException("Нельзя суммировать деньги в разных валютах");
return new Money(a.Amount + b.Amount, a.Unit);
}
public static Money operator ++(Money a) // перегрузка «++»
{
a.Amount++;
return a;
}
public static Money operator --(Money a) // перегрузка «--»
{
a.Amount--;
return a;
}
}
class Program
{
static void Main(string[] args)
{
Money myMoney = new Money(100, "USD");
myMoney++; // 101 USD
Console.ReadLine();
}
}
Также существует возможность перегрузки самого операторного метода. Это означает что в классе может быть несколько перегрузок одного оператора при условии что входные параметры будут отличаться типом данных (например, когда необходимо сложить объект класса и строку):
public class Money
{
public decimal Amount { get; set; }
public string Unit { get; set; }
public Money(decimal amount, string unit)
{
Amount = amount;
Unit = unit;
}
public static Money operator +(Money a, Money b)
{
if (a.Unit != b.Unit)
throw new InvalidOperationException("Нельзя суммировать деньги в разных валютах");
return new Money(a.Amount + b.Amount, a.Unit);
}
public static string operator +(string text, Money a)
{
return text + a.Amount + " " + a.Unit;
}
}
class Program
{
static void Main(string[] args)
{
Money myMoney = new Money(100, "USD");
Console.WriteLine("У меня сейчас " + myMoney); // "У меня сейчас 100 USD"
Console.ReadLine();
}
}
Домашнее задание
Задание будет базироваться на примере в этом уроке. Необходимо реализовать второй вариант сложения денег – чтобы можно было суммировать деньги в разных валютах. Для этого создайте отдельный класс, который будет предоставлять механизм конвертации денег по заданному курсу. Кроме этого, перегрузите для класса Money оператор сравнения «==» (при перегрузке данного оператора, обязательной является и перегрузка противоположного ему оператора «!=»).
PS. Не забудьте поделиться с друзьями этой статьей с помощью кнопок социальных сетей.
Переопределение методов Equals и GetHashCode в Си-шарп. Разница между Equals и «==» Как известно, в Си-шарп все классы являются наследниками базового класса object. В нем есть три виртуальных метода – ToString, Equals и GetHashCode. В этом уроке мы поговорим с вами о последних двух методах, а также об операторе «==».
Скажу сразу, что вопрос разницы между оператором равенства «==» и методом Equals является классическим вопросом на собеседовании на вакансию программиста Си-шарп.
Оператор равенства «==»
По умолчанию при работе с ссылочными типами данных (все классы кроме string, интерфейсы, делегаты) оператор «==» проверяет равенство ссылок. Он возвращает true, когда обе ссылки указывают на один объект, в противном случае – false. Приведу код, который демонстрирует работу данного оператора с ссылочными типами:
static void Main(string[] args)
{
object o1 = new object();
object o2 = new object();
object o3 = o1;
Console.WriteLine(o1 == o2); // false
Console.WriteLine(o1 == o3); // true
}
Здесь создается два объекта, ссылки на которые записываются в переменные o1 и o2. Дальше ссылка o1 копируется в переменную o3 (o1 и o3 указывают на один объект). В итоге имеем false при сравнении ссылок o1 и o2, и true при o1 и o3.
Метод Equals
Метод Equals принимает один аргумент – объект, который будет сравниваться с текущим объектом, и определяет, равны ли между собой эти объекты. Здесь уже идет речь о равенстве полей объектов, а не ссылок. Этот метод виртуальный, и его базовая реализация это просто проверка равенства ссылок оператором «==». Но когда мы создаем некий класс, и нам необходимо реализовать возможность проверки идентичности объектов, следует переопределить именно данный метод, а не воспользоваться перегрузкой оператора «==», чтобы не спутывать базовые назначения этих инструментов сравнивания.
Перегрузка метода Equals
При переопределении метода Equals следует позаботиться о том, чтобы этот метод возвращал false в случаях, когда в метод передано значение NULL, когда переданный объект нельзя привести к типу текущего объекта, ну и когда поля объектов отличаются.
Возьмем уже знакомый нам класс Money с предыдущего урока, и переопределим в нем метод Equals:
public class Money
{
public decimal Amount { get; set; }
public string Unit { get; set; }
public Money(decimal amount, string unit)
{
Amount = amount;
Unit = unit;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
Money m = obj as Money; // возвращает null если объект нельзя привести к типу Money
if (m as Money == null)
return false;
return m.Amount == this.Amount && m.Unit == this.Unit;
}
}
class Program
{
static void Main(string[] args)
{
Money m1 = new Money(100, "RUR");
Money m2 = new Money(100, "RUR");
Money m3 = new Money(100, "USD");
Money m4 = m1;
Console.WriteLine(m1.Equals(m2)); // true
Console.WriteLine(m1.Equals(m3)); // false
Console.WriteLine(m1 == m2); // false
Console.WriteLine(m1 == m4); // true
Console.ReadLine();
}
}
Как видим, в коде выше метод Equals и оператор «==» работают соответственно своим базовым определениям.
Также для повышения производительности при переопределении метода Equalsрекомендуется перегружать его реализацией с типом аргумента соответствующему классу, в котором он переопределяется:
public bool Equals(Money obj) // аргумент типа Money
{
if (obj == null)
return false;
return obj.Amount == this.Amount && obj.Unit == this.Unit;
}
Метод GetHashCode
Данный метод, как следует из его названия, возвращает хеш-код. Хеш-код это число соответствующее значению объекта. Это число мы получаем в результате работы некоторого метода, который должен обладать следующими свойствами:
- он должен возвращать одинаковый хеш-код каждый раз при вызове для одного и того же объекта.
- если имеется два равных (эквивалентных) объекта, то хеш-код для них должен быть одинаковым. Только это не означает, что если объекты неравны, то их хеш-коды обязательно будут разными.
Метод GetHashCode используется в таких структурах, как хэш-таблицы (Hashtable). Это мы сейчас рассматривать не будем, но корректность их работы стоит обеспечивать. Методы Equals и GetHashCode тесно связанны между собой, при переопределении одного из них, следует переопределять и другой. Базовая реализация метода GetHashCode в классе object очень условная, и она не обеспечивает второе свойство, когда одинаковые объекты имеют одинаковые хеш-коды.
static void Main(string[] args)
{
Money m1 = new Money(100, "RUR");
Money m2 = new Money(100, "RUR");
Console.WriteLine(m1.GetHashCode()); // 456...
Console.WriteLine(m2.GetHashCode()); // 411...
Console.ReadLine();
}
Чтобы это исправить, мы переопределяем метод GetHashCode, и возвращаем хеш-код каким-либо способом, зависящим от поля/полей объекта:
public class Money
{
public decimal Amount { get; set; }
public string Unit { get; set; }
public Money(decimal amount, string unit)
{
Amount = amount;
Unit = unit;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
Money m = obj as Money;
if (m as Money == null)
return false;
return m.Amount == this.Amount && m.Unit == this.Unit;
}
public bool Equals(Money obj) // аргумент типа Money
{
if (obj == null)
return false;
return obj.Amount == this.Amount && obj.Unit == this.Unit;
}
public override int GetHashCode()
{
int unitCode;
if (Unit == "RUR")
unitCode = 1;
else unitCode = 2;
return (int) Amount + unitCode;
}
}
class Program
{
static void Main(string[] args)
{
Money m1 = new Money(100, "RUR");
Money m2 = new Money(100, "RUR");
Money m3 = new Money(100, "USD");
Console.WriteLine(m1.GetHashCode()); // 101
Console.WriteLine(m2.GetHashCode()); // 101
Console.WriteLine(m3.GetHashCode()); // 102
Console.ReadLine();
}
}
Здесь в качестве хеш-кода возвращается количество денег (целая часть) плюс код валюты. В результате теперь второе условие выполняется.
Домашнее задание
Создайте класс окружность с полями координаты центра и радиус и переопределите в нем корректно методы Equals и GetHashCode. Окружности равны если у них одинаковые координаты центра и радиусы.
Регулярные выражения в Си-шарп. Класс Regex В этом уроке мы рассмотрим с вами очень мощный, широко используемый инструмент для обработки текста – регулярные выражения.
Регулярное выражение – это некий шаблон, составленный из символов и спецсимволов, который позволяет находить подстроки соответствующие этому шаблону в других строках. Спецсимволов и различных правил их комбинирования есть очень много, поэтому регулярные выражения можно даже назвать таким себе отдельным языком программирования. Те, кто пользовался поиском по файлам в Windows могут знать, что для того чтобы найти файлы только заданного расширения, задается шаблон типа «*.txt». Здесь «*» - спецсимвол, который означает любые имена файлов. Так вот регулярные выражения предоставляют подобный механизм.