Equals メソッドの実装

等値演算子 (==) の実装の関連情報については、「Equals および等値演算子 (==) 実装のガイドライン」を参照してください。

  • 型がハッシュ テーブルで正常に機能するように、GetHashCode メソッドをオーバーライドします。

  • Equals メソッドの実装では例外をスローしないでください。 代わりに、null 引数が渡された場合は false を返します。

  • 次のように、Object.Equals メソッドについて定義されている規則に従います。

    • x.Equals(x) は true を返します。

    • x.Equals(y) は、y.Equals(x) と同じ値を返します。

    • (x.Equals(y) && y.Equals(z)) は、x.Equals(z) が true を返す場合にだけ true を返します。

    • x.Equals(y) が連続して呼び出された場合は、x および y が参照するオブジェクトが変更されていない限り、同じ値を返します。

    • x.Equals(null) は、false を返します。

  • オブジェクトの種類によっては、Equals で参照が等しいかどうかではなく、値が等しいかどうかを調べる必要があります。 このような Equals の実装は、2 つのオブジェクトが同じインスタンスではなくても、同じ値を持つ場合に true を返します。 オブジェクトの値を構成する要素は、型の実装者が定義しますが、通常は、オブジェクトのインスタンス変数に格納されたデータの一部またはすべてで構成されます。 たとえば、文字列の値は、文字列内の文字に基づきます。String クラスの Equals メソッドは、同じ文字が正確に同じ順序で含まれている 2 つの文字列インスタンスについて true を返します。

  • 基本クラスの Equals メソッドが値が等しいかどうかを調べる場合、派生クラスの Equals のオーバーライドは、継承された Equals の実装を呼び出す必要があります。

  • 演算子のオーバーロードをサポートする言語でプログラミングするとき、特定の型に対して等値演算子 (==) をオーバーライドする場合は、その型で Equals メソッドをオーバーライドする必要があります。 Equals メソッドのこのような実装は、等値演算子と同じ結果を返す必要があります。 このガイドラインに準拠することによって、Equals を使用するクラス ライブラリ コード (ArrayListHashtable など) と、アプリケーション コードで使用される等値演算子の動作の一貫性が維持されます。

  • 値型を実装する場合は、ValueType に対する Equals メソッドの既定の実装よりも高いパフォーマンスが得られるように、Equals メソッドをオーバーライドすることを検討してください。 Equals をオーバーライドするときに、言語が演算子のオーバーロードをサポートする場合は、その値型の等値演算子もオーバーライドする必要があります。

  • 参照型を実装するとき、この型が Point、String、BigNumber などの基本型のように見える場合は、参照型の Equals メソッドをオーバーライドすることを検討します。 ほとんどの参照型は、Equals をオーバーライドする場合でも、等値演算子はオーバーライドしません。 ただし、複雑な数値型など、値のセマンティクスを持つ参照型を実装する場合は、等値演算子をオーバーライドする必要があります。

  • 指定した型で IComparable インターフェイスを実装する場合は、その型で Equals をオーバーライドします。

Equals メソッドの実装、呼び出し、オーバーライド、およびオーバーロードを行うコード例を次に示します。

Equals メソッドの実装

Equals メソッドの既定の実装が 2 回呼び出されているコード例を次に示します。

Imports System
Class SampleClass   
   Public Shared Sub Main()
      Dim obj1 As New System.Object()
      Dim obj2 As New System.Object()
      Console.WriteLine(obj1.Equals(obj2))
      obj1 = obj2
      Console.WriteLine(obj1.Equals(obj2))
   End Sub
End Class
using System;
class SampleClass 
{
   public static void Main() 
   {
      Object obj1 = new Object();
      Object obj2 = new Object();
      Console.WriteLine(obj1.Equals(obj2));
      obj1 = obj2; 
      Console.WriteLine(obj1.Equals(obj2)); 
   }
}

このコードによる出力は、次のようになります。

False
True

Equals メソッドのオーバーライド

Equals メソッドをオーバーライドして値が等しいかどうかを調べる Point クラスと、Point から派生される Point3D クラスのコード例を次に示します。 Point クラスの Equals のオーバーライドが継承チェーンの先頭にあり、値が等しいかどうかのチェックを導入するため、基本クラスの Equals メソッド (Object から継承され、参照が等しいかどうかをチェックする) は呼び出されません。 しかし、Point が値を等しいかどうかを調べるように Equals を実装しているため、Point3D.Equals は Point.Equals を呼び出します。

Namespace Examples.DesignGuidelines.EqualsImplementation

Public Class Point  
   Protected x As Integer
   Protected y As Integer

   Public Sub New (xValue As Integer, yValue As Integer)
    Me.x = xValue
    Me.y = yValue
   End Sub

   Public Overrides Overloads Function Equals(obj As Object) As Boolean

      If obj Is Nothing OrElse Not Me.GetType() Is obj.GetType() Then
         Return False
      End If

      Dim p As Point = CType(obj, Point)
      Return Me.x = p.x And Me.y = p.y
   End Function 

   Public Overrides Function GetHashCode() As Integer
      Return x Xor y
   End Function 
End Class 

Public Class Point3D
   Inherits Point
   Private z As Integer

   Public Sub New (xValue As Integer, yValue As Integer, zValue As Integer)
      MyBase.New(xValue, yValue)
      Me.z = zValue
   End Sub

   Public Overrides Overloads Function Equals(obj As Object) As Boolean
      Return MyBase.Equals(obj) And z = CType(obj, Point3D).z
   End Function 

   Public Overrides Function GetHashCode() As Integer
      Return MyBase.GetHashCode() Xor z
   End Function 
End Class 

End Namespace

using System;

namespace Examples.DesignGuidelines.EqualsImplementation
{
class Point: object 
{
   protected int x, y;

   public Point(int xValue, int yValue)
   {
        x = xValue;
        y = yValue;
   }
   public override bool Equals(Object obj) 
   {
      // Check for null values and compare run-time types.
      if (obj == null || GetType() != obj.GetType()) 
         return false;

      Point p = (Point)obj;
      return (x == p.x) && (y == p.y);
   }
   public override int GetHashCode() 
   {
      return x ^ y;
   }
}

class Point3D: Point 
{
   int z;

   public Point3D(int xValue, int yValue, int zValue) : base(xValue, yValue)
   {
        z = zValue;
   }
   public override bool Equals(Object obj) 
   {
      return base.Equals(obj) && z == ((Point3D)obj).z;
   }
   public override int GetHashCode() 
   {
      return base.GetHashCode() ^ z;
   }
}
}
using namespace System;

namespace Examples { namespace DesignGuidelines { namespace EqualsImplementation
{
    ref class Point : Object
    {
    protected:
        int x, y;

    public:
        Point(int xValue, int yValue)
        {
            x = xValue;
            y = yValue;
        }

        virtual bool Equals(Object^ obj) override
        {
            // Check for null values and compare run-time types.
            if (obj == nullptr || GetType() != obj->GetType())
            {
                return false;
            }

            Point^ p = (Point^)obj;

            return (x == p->x) && (y == p->y);
       }

       virtual int GetHashCode() override
       {
           return x ^ y;
       }
    };

    ref class Point3D : Point
    {
    private:
        int z;

    public:
        Point3D(int xValue, int yValue, int zValue) : Point(xValue, yValue)
        {
            z = zValue;
        }

        virtual bool Equals(Object^ obj) override
        {
            return Point::Equals(obj) && z == ((Point3D^)obj)->z;
        }

        virtual int GetHashCode() override
        {
            return Point::GetHashCode() ^ z;
        }
    };
}}}

Point.Equals メソッドは、obj 引数が null 以外であることと、対象のオブジェクトと同じ型のインスタンスを参照していることを確認します。 いずれかの確認が失敗すると、このメソッドは false を返します。 Equals メソッドは GetType メソッドを使用して、2 つのオブジェクトのランタイム型が同一かどうかを判断します。 typeof (Visual Basic では TypeOf) は静的な型を返すため、ここでは使用されていません。 代わりに obj is Point の形式でチェックを行うメソッドは、obj と現在のインスタンスが同じランタイム型でなくても、obj が Point から派生されたクラスのインスタンスである場合は true を返します。 両方のオブジェクトが同じ型であることを確認した後、obj は型 Point にキャストされ、2 つのオブジェクトのインスタンス変数を比較した結果が返されます。

Point3D.Equals では、まず、継承された Equals メソッドが呼び出されます。 継承された Equals メソッドは、obj が null でないことを確認し、obj が対象のオブジェクトと同じクラスのインスタンスであることを確認し、継承されたインスタンス変数が一致することを確認します。 継承された Equalstrue を返したときにだけ、派生クラスに導入されたインスタンス変数が比較されます。 特に、Point3D へのキャストは、obj の型が Point3D または Point3D から継承されたクラスであると判断された場合にだけ実行されます。

Equals メソッドを使用したインスタンス変数の比較

前の例では、等値演算子 (==) を使用して個別のインスタンス変数を比較しました。 次の例に示すように、Equals メソッドを使用して Equals 実装の中でインスタンス変数を比較する方が適切な場合もあります。

Imports System

Class Rectangle
   Private a, b As Point
   
   Public Overrides Overloads Function Equals(obj As [Object]) As Boolean
      If obj Is Nothing Or Not Me.GetType() Is obj.GetType() Then
         Return False
      End If
      Dim r As Rectangle = CType(obj, Rectangle)
      ' Use Equals to compare instance variables.
      Return Me.a.Equals(r.a) And Me.b.Equals(r.b)
   End Function 
   
   Public Overrides Function GetHashCode() As Integer
      Return a.GetHashCode() ^ b.GetHashCode()
   End Function 
End Class 
using System;
class Rectangle 
{
   Point a, b;
   public override bool Equals(Object obj) 
   {
      if (obj == null || GetType() != obj.GetType()) return false;
      Rectangle r = (Rectangle)obj;
      // Use Equals to compare instance variables.
      return a.Equals(r.a) && b.Equals(r.b);
   }
   public override int GetHashCode() 
   {
      return a.GetHashCode() ^ b.GetHashCode();
   }
}

等値演算子 (==) と Equals メソッドのオーバーロード

C# などの一部のプログラミング言語では、演算子のオーバーロードがサポートされています。 等値演算子 (==) をオーバーロードするときは、Equals メソッドも同じ機能を使用できるようにオーバーロードする必要があります。 通常は、次のコード例で示すように、オーバーロードされた等値演算子 (==) に置き換えて Equals メソッドを記述します。

public struct Complex 
{
   double re, im;
   public override bool Equals(Object obj) 
   {
      return obj is Complex && this == (Complex)obj;
   }
   public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }
   public static bool operator ==(Complex x, Complex y) 
   {
      return x.re == y.re && x.im == y.im;
   }
   public static bool operator !=(Complex x, Complex y) 
   {
      return !(x == y);
   }
}

Complex は C# の struct (値型) であるため、Complex からはクラスが派生されません。 そのため、Equals メソッドは、オブジェクトごとに GetType の結果を比較する必要がありません。 代わりに、is 演算子を使用して obj パラメーターの型を確認します。

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

設計ガイドラインの詳細についてを参照してください、「フレームワークの設計ガイドライン。規則、慣用句、および再利用可能なパターン。ネット ライブラリ」本クシシュトフ Cwalina、ブラッド エイブラムス、アスキー、2005 年発表しました。

参照

その他の技術情報

クラス ライブラリ開発のデザイン ガイドライン