web-dev-qa-db-ja.com

==演算子をオーバーライドします。 nullと比較する方法は?

可能性のある複製:
無限再帰なしの「==」演算子オーバーロードでヌルをチェックするにはどうすればよいですか?

これにはおそらく簡単な答えがあります...しかし、それは私を避けているようです。簡単な例を次に示します。

public class Person
{
   public string SocialSecurityNumber;
   public string FirstName;
   public string LastName;
}

この特定のアプリケーションについて、社会保障番号が一致し、両方の名前が一致する場合、同じ「人」を参照していると言うのは有効だとしましょう。

public override bool Equals(object Obj)
{
    Person other = (Person)Obj;
    return (this.SocialSecurityNumber == other.SocialSecurityNumber &&
        this.FirstName == other.FirstName &&
        this.LastName == other.LastName);
}

一貫性を保つために、.Equalsメソッドを使用しないチームの開発者向けに、==および!=演算子もオーバーライドします。

public static bool operator !=(Person person1, Person person2)
{
    return ! person1.Equals(person2);
}

public static bool operator ==(Person person1, Person person2)
{
    return person1.Equals(person2);
}

元気でいいですか?

ただし、Personオブジェクトがnullの場合はどうなりますか?

あなたは書くことができません:

if (person == null)
{
    //fail!
}

これにより==演算子のオーバーライドが実行されるため、次のコードは失敗します。

person.Equals()

nullインスタンスでメソッドを呼び出すことができないため、メソッド呼び出し。

一方、==オーバーライド内でこの条件を明示的にチェックすることはできません。無限再帰(およびスタックオーバーフロー[dot com])が発生するためです。

public static bool operator ==(Person person1, Person person2)
{
    if (person1 == null)
    {
         //any code here never gets executed!  We first die a slow painful death.
    }
    return person1.Equals(person2);
}

それでは、値の等価性のために==および!=演算子をオーバーライドし、それでもヌルオブジェクトをどのように説明しますか?

答えが非常に単純ではないことを願っています。 :-)

125
Flipster

_==_演算子の代わりにobject.ReferenceEquals(person1, null)を使用します。

_public static bool operator ==(Person person1, Person person2)
{
    if (object.ReferenceEquals(person1, null))
    {
         return object.ReferenceEquals(person2, null);
    }

    return person1.Equals(person2);
}
_
234
cdhowie

(==および!=演算子に対して)常にこの方法でこれを行っており、作成するすべてのオブジェクトに対してこのコードを再利用しています。

public static bool operator ==(Person lhs, Person rhs)
{
    // If left hand side is null...
    if (System.Object.ReferenceEquals(lhs, null))
    {
        // ...and right hand side is null...
        if (System.Object.ReferenceEquals(rhs, null))
        {
            //...both are null and are Equal.
            return true;
        }

        // ...right hand side is not null, therefore not Equal.
        return false;
    }

    // Return true if the fields match:
    return lhs.Equals(rhs);
}

「!=」は次のようになります。

public static bool operator !=(Person lhs, Person rhs)
{
    return !(lhs == rhs);
}

編集
変更した==マイクロソフトの提案する実装​​に一致する演算子関数 ここ

17
Mike Webb

あなたは常にオーバーライドして置くことができます

(Object)(person1)==null

確かではありませんが、これでうまくいくと思います。

11
rtpg

最後の(仮想の)ルーチンは次のとおりです。 @cdhowieが最初に受け入れた応答と非常によく似ています。

_public static bool operator ==(Person person1, Person person2)
{
    if (Person.ReferenceEquals(person1, person2)) return true;
    if (Person.ReferenceEquals(person1, null)) return false; //*
    return person1.Equals(person2);
}
_

素晴らしい反応をありがとう!

// *-.Equals()はperson2でnullチェックを実行します

3
Flipster

Personインスタンスをobjectにキャストします。

public static bool operator ==(Person person1, Person person2)
{
    if ((object)person1 == (object)person2) return true;
    if ((object)person1 == null) return false;
    if ((object)person2 == null) return false;
    return person1.Equals(person2);
}
2
dtb

これらのアプローチのいずれよりも簡単に使用することです

public static bool operator ==(Person person1, Person person2)   
{   
    EqualityComparer<Person>.Default.Equals(person1, person2)
} 

これは、他の誰もが提案しているアプローチと同じヌル等値セマンティクスを持っていますが、詳細を理解することはフレームワークの問題です:)

1
start

これらの演算子を一貫してオーバーロードすることは非常に困難です。 関連する質問への私の答え はテンプレートとして機能します。

基本的に、最初に参照を行う必要があります(object.ReferenceEquals)オブジェクトがnullかどうかをテストします。 その後、Equalsを呼び出します。

1
Konrad Rudolph

cdhowieはReferenceEqualsを使用してお金を稼いでいますが、誰かがnullEqualsに直接渡すと例外が発生する可能性があることに注意してください。また、Equalsをオーバーライドする場合、ほとんど常に_IEquatable<T>_を実装する価値があるので、代わりに使用します。

_public class Person : IEquatable<Person>
{
  /* more stuff elided */

  public bool Equals(Person other)
  {
    return !ReferenceEquals(other, null) &&
      SocialSecurityNumber == other.SocialSecurityNumber &&
      FirstName == other.FirstName &&
      LastName == other.LastName;
  }
  public override bool Equals(object obj)
  {
    return Equals(obj as Person);
  }
  public static bool operator !=(Person person1, Person person2)
  {
    return !(person1 == person2);
  }
  public static bool operator ==(Person person1, Person person2)
  {
    return ReferenceEquals(person1, person2)
      || (!ReferenceEquals(person1, null) && person1.Equals(person2));
  }
}
_

そしてもちろん、Equalsをオーバーライドしてはならず、GetHashCode()をオーバーライドしないでください。

_public override int GetHashCode()
{
   //I'm going to assume that different
   //people with the same SocialSecurityNumber are extremely rare,
   //as optimise by hashing on that alone. If this isn't the case, change this
   return SocialSecurityNumber.GetHashCode();
}
_

また、同一性には平等が必要であることにも注意してください(つまり、「平等」の有効な概念では、何かは常にそれ自体に等しくなります)。等価性テストは高価でループ内で発生する可能性があり、実際のコードで何かを比較することは非常に一般的であるため(特にオブジェクトが複数の場所で渡される場合)、ショートカットとして追加する価値があります:

_  public bool Equals(Person other)
  {
    return !ReferenceEquals(other, null) &&
      ReferenceEquals(this, other) ||
      (
        SocialSecurityNumber == other.SocialSecurityNumber &&
        FirstName == other.FirstName &&
        LastName == other.LastName
      );
  }
_

ReferenceEquals(this, other)のショートカットの大きさは、クラスの性質によってかなり異なりますが、それを行う価値があるかどうかは常に考慮する必要があるため、このテクニックを含めます。ここに。

1
Jon Hanna

Personをオブジェクトにキャストしてから、比較を実行します。

object o1 = (object)person1;
object o2 = (object)person2;
if(o1==o2) //compare instances.
   return true;
if (o1 == null || o2 == null)  //compare to null.
   return false;
//continue with Person logic.
1
Greg Sansom