読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET 絡みのメモを公開しています。

.NET クラスライブラリ探訪-056 (System.IEquatable, 等価性, 型指定のEqualsメソッド)

C#


今回は、System.IEquatableインターフェースについて
ちょこっとメモメモ。


このインターフェースは、よく利用されるので今回はメモはちょこっとだけ。


IEquatableインターフェースは、2つのインスタンスが等しいか否かを判別するための
型指定のEqualsメソッドを定義しているインターフェースです。

このインターフェースを実装することで、通常のobject.Equals以外に型が指定された
Equalsメソッドを持つことができるようになります。


このインターフェース、特に構造体を定義する時に重要でして、構造体の場合、object.Equalsで
比較を行うと、ボックス化が発生してしまうため、IEquatableを実装することが多いです。
(ボックス化が発生しなくなるため。)

また、厳密には必須ではありませんが、IEquatableを実装する場合、同時に以下のメソッドもオーバーライドし一貫性を保つようにします。
・object.Equals
・object.GetHashCode
object.Equalsをオーバーライドするのは、IEquatableを実装していてもクラスによっては、それを無視して強制的にobject.Equalsで
比較する処理が存在するためです。

IEquatableインターフェースは、Dictionary, Listなどのジェネリックコレクションにて
Contains, IndexOf, LastIndexOf, Removeなどの各メソッドで等価性をテストする場合に利用されます。
(ArrayのIndexOfメソッドなどでも同様に利用されます。)


以下サンプルです。

  #region IEquatableSamples-01
  /// <summary>
  /// IEquatable<T>のサンプルです。
  /// </summary>
  public class IEquatableSamples01 : IExecutable
  {
    public void Execute()
    {
      //
      // IEquatable<T>インターフェースは、2つのインスタンスが等しいか否かを判別するための
      // 型指定のEqualsメソッドを定義しているインターフェースである。
      //
      // このインターフェースを実装することで、通常のobject.Equals以外に型が指定された
      // Equalsメソッドを持つことができるようになる。
      // このインターフェースは、特に構造体を定義する上で重要であり、構造体の場合、object.Equalsで
      // 比較を行うと、ボックス化が発生してしまうため、IEquatable<T>を実装することが多い。
      // (ボックス化が発生しなくなるため。)
      //
      // また、厳密には必須ではないが、IEquatable<T>を実装する場合、同時に以下のメソッドもオーバーライドするのが普通である。
      //   ・object.Equals
      //   ・object.GetHashCode
      // object.Equalsをオーバーライドするのは、IEquatableを実装していてもクラスによっては、それを無視して強制的にobject.Equalsで
      // 比較する処理が存在するためである。
      //
      // IEquatable<T>インターフェースは、Dictionary<TKey, TValue>, List<T>などのジェネリックコレクションにて
      // Contains, IndexOf, LastIndexOf, Removeなどの各メソッドで等価性をテストする場合に利用される。
      // (ArrayのIndexOfメソッドなどでも同様に利用される。)
      // 同じインターフェースで、比較機能を提供するものとして、IComparable<T>インターフェースがある。
      //
      // object.GetHashCodeをオーバーライドするのは、上の理由によりobject.Equalsがオーバーライドされるため。
      //
      Data data1 = new Data(1, "Hello World");
      Data data2 = new Data(2, "Hello World2");
      Data data3 = new Data(3, "Hello World3");
      Data data4 = data3;
      Data data5 = new Data(1, "Hello World4");
      
      Console.WriteLine("data1 equals data2? ==> {0}", data1.Equals(data2));
      Console.WriteLine("data1 equals data3? ==> {0}", data1.Equals(data3));
      Console.WriteLine("data1 equals data4? ==> {0}", data1.Equals(data4));
      Console.WriteLine("data1 equals data5? ==> {0}", data1.Equals(data5));
      
      object d1 = data1;
      object d2 = data2;
      object d5 = data5;
      
      Console.WriteLine("data1 equals data2? ==> {0}", d1.Equals(d2));
      Console.WriteLine("data1 equals data5? ==> {0}", d1.Equals(d5));
      
      Data[] dataArray = { data1, data2, data3, data4, data5 };
      Console.WriteLine("IndexOf={0}", Array.IndexOf(dataArray, data3));
    }
    
    sealed class Data : IEquatable<Data>
    {
      public Data(int id, string name)
      {
        Id = id;
        Name = name;
      }
      
      public int Id
      {
        get;
        private set;
      }
      
      public string Name
      {
        get;
        private set;
      }
      
      // IEquatable<T>の実装.
      public bool Equals(Data other)
      {
        Console.WriteLine("\t→→Call IEquatable.Equals");
        
        if (other == null)
        {
          return false;
        }
        
        return Id == other.Id;
      }
      
      // object.Equals
      public override bool Equals(object other)
      {
        Console.WriteLine("\t→→Call object.Equals");
        
        Data data = other as Data;
        if (data == null)
        {
          return false;
        }
        
        return Equals(data);
      }
      
      // object.GetHashCode
      public override int GetHashCode()
      {
        return Id.GetHashCode();
      }
    }
  }
  #endregion


以下、実行結果です。

  	→→Call IEquatable.Equals
  data1 equals data2? ==> False
  	→→Call IEquatable.Equals
  data1 equals data3? ==> False
  	→→Call IEquatable.Equals
  data1 equals data4? ==> False
  	→→Call IEquatable.Equals
  data1 equals data5? ==> True
  	→→Call object.Equals
  	→→Call IEquatable.Equals
  data1 equals data2? ==> False
  	→→Call object.Equals
  	→→Call IEquatable.Equals
  data1 equals data5? ==> True
  	→→Call IEquatable.Equals
  	→→Call IEquatable.Equals
  	→→Call IEquatable.Equals
  IndexOf=2


以下、参考資料です。

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。