MyTetra Share
Делитесь знаниями!
Сравнение собственных классов
Время создания: 02.11.2019 00:36
Текстовые метки: Сравнение, Equals, GetHashCode
Раздел: Компьютер - C#
Запись: Kozlov-AE/Tetra/master/base/1572644218hpdffgf15i/text.html на raw.githubusercontent.com

Как известно, в Си-шарп все классы являются наследниками базового класса 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();

   }
}

Здесь в качестве хеш-кода возвращается количество денег (целая часть) плюс код валюты. В результате теперь второе условие выполняется.

Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования