148

重複の可能性:
無限再帰を使用せずに「==」演算子のオーバーロードで 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 インスタンスでメソッドを呼び出すことはできないためです。

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

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);
}

では、値の等価性のために == および != 演算子をオーバーライドし、それでも null オブジェクトを考慮するにはどうすればよいでしょうか?

答えが痛々しいほど単純ではないことを願っています。:-)

4

9 に答える 9

268

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

public static bool operator ==(Person person1, Person person2)
{
    if (person1 is null)
    {
         return person2 is null;
    }

    return person1.Equals(person2);
}
于 2010-11-18T20:31:38.540 に答える
21

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

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);
}

編集マイクロソフトの提案された実装here
に一致するように演算子関数 を変更しました。==

于 2010-11-18T20:38:38.577 に答える
12

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

(Object)(person1)==null

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

于 2010-11-18T20:32:26.173 に答える
5

これらのアプローチのどれよりも簡単に使用できます

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

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

于 2010-11-19T02:17:23.857 に答える
3

最終的な (仮想の) ルーチンは以下のとおりです。@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 チェックを実行します

于 2010-11-18T21:26:45.073 に答える
2

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);
}
于 2010-11-18T20:33:47.517 に答える
2

Person を Object にキャストしてから、比較を実行します。

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.
于 2010-11-18T20:36:00.870 に答える
1

これらの演算子を一貫してオーバーロードするのは非常に困難です。関連する質問に対する私の回答は、テンプレートとして役立つ場合があります。

基本的に、最初にリファレンス ( object.ReferenceEquals) テストを実行して、オブジェクトが であるかどうかを確認する必要がありますnull次に、 を呼び出しますEquals

于 2010-11-18T20:33:24.397 に答える
1

cdhowie は を使用してお金を稼いでいますが、誰かがに直接ReferenceEquals渡した場合でも例外が発生する可能性があることに注意してください。また、オーバーライドする場合は、ほとんどの場合実装する価値があるので、代わりに実装します。nullEqualsEqualsIEquatable<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)は、授業の性質によって大きく異なりますが、やっていることの価値があるかどうかは常に考えなければならないので、ここにテクニックを含めます。

于 2010-11-19T01:25:48.297 に答える