6

次のコードを使用して、IEquatableインターフェイスをクラスに実装しました。

        public bool Equals(ClauseBE other)
        {
            if (this._id == other._id)
            {
                return true;
            }
            return false;
        }

        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return base.Equals(obj);
            }

            if (!(obj is ClauseBE))
            {
                throw new InvalidCastException("The 'obj' argument is not a ClauseBE object.");
            }

            return Equals(obj as ClauseBE);
        }

        public override int GetHashCode()
        {
            return this._id.GetHashCode();
        }

        public static bool operator ==(ClauseBE a, ClauseBE b)
        {
            // cast to object so we call the overloaded Equals function which appropriately checks when b is null.
            return a.Equals(b as object);
        }

        public static bool operator !=(ClauseBE a, ClauseBE b)
        {
            // cast to object so we call the overloaded Equals function which appropriately checks when b is null.
            return !a.Equals(b as object);
        }

このコードは、ほとんどすべての場合に非常にうまく機能します。ただし、次のチェックでは、aがnullであり、Equalsメソッドがないため、等式演算子のオーバーロードメソッドで例外がスローされます。

if(this.Clause != null)
{

}

この問題を解決するための標準的な方法は何ですか?

編集

私はこれに行きましたが、それはかなり面倒なようです。これを達成するためのよりエレガントな方法があることを望んでいました。

    public static bool operator ==(ClauseBE a, ClauseBE b)
    {
        if (a as object == null && b as object == null)
        {
            return true;
        }

        if ((a as object == null && b as object != null)
            || (b as object == null && a as object != null))
        {
            return false;
        }

        // cast to object so we call the overloaded Equals function which appropriately checks when b is null.
        return a.Equals(b as object);
    }

    public static bool operator !=(ClauseBE a, ClauseBE b)
    {
        if (a as object == null && b as object == null)
        {
            return false;
        }

        if((a as object == null && b as object != null)
            || (b as object == null && a as object != null))
        {
            return true;
        }

        // cast to object so we call the overloaded Equals function which appropriately checks when b is null.
        return !a.Equals(b as object);
    }

解決

皆さんありがとう。みんなからたくさんの良いアドバイスをもらいました。本当に感謝しています。これは私が最終的に決めたものであり、私が始めたものよりもはるかにエレガントです。演算子のオーバーロードを除いて、すべてのコードは同じです。

public static bool operator ==(ClauseBE a, ClauseBE b)
{
    if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
    {
        return true;
    }

    if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
    {
        return false;
    }

    return a.Equals(b);
}

public static bool operator !=(ClauseBE a, ClauseBE b)
{
    return !(a == b);
}
4

8 に答える 8

5

null処理を使用して静的演算子を記述し、Equalsオーバーライドで、パラメーターの1つとして「this」を使用してオーバーロードされた演算子を呼び出す方が簡単であることが常にわかりました。

Equals()とOperator ==のオーバーロードに関するガイドラインから(C#プログラミングガイド)

//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)a == null) || ((object)b == null))
    {
        return false;
    }

    // Return true if the fields match:
    return a.x == b.x && a.y == b.y && a.z == b.z;
}

public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
    return !(a == b);
}
于 2009-06-09T22:25:59.390 に答える
2

これは、ReSharperが等式演算子を作成して実装する方法IEquatable<T>です。もちろん、私は盲目的に信頼しています;-)

public class ClauseBE : IEquatable<ClauseBE>
{
    private int _id;

    public bool Equals(ClauseBE other)
    {
        if (ReferenceEquals(null, other))
            return false;
        if (ReferenceEquals(this, other))
            return true;
        return other._id == this._id;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj.GetType() != typeof(ClauseBE))
            return false;
        return Equals((ClauseBE)obj);
    }

    public override int GetHashCode()
    {
        return this._id.GetHashCode();
    }

    public static bool operator ==(ClauseBE left, ClauseBE right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(ClauseBE left, ClauseBE right)
    {
        return !Equals(left, right);
    }
}
于 2009-06-09T22:54:14.980 に答える
1

これは、nullをチェックする前にObjectにキャストするよりも少し面倒ではないと思います。

ReferenceEquals(a, null)
于 2009-06-09T22:41:15.943 に答える
1

nullをチェックし、falseを返します。オペランドの1つがnullの場合、Equalsは常にfalseである必要があります。

于 2009-06-09T22:26:20.420 に答える
1

他の答えは、一般的な問題に対する良い解決策を与えます。

ただし、独自のコードを単純化して比較的単純なソリューションにすることができます...

まず、==オペレーターの開始時に次のようになります。

    // First test
    if (a as object == null && b as object == null)
    {
        return true;
    }

これは「一生懸命働いている」と見なされます。

が参照型の場合ClauseBE、比較する必要があるnullのは-" as object"は冗長です。同様に、ClauseBEが値型の場合、それは決してnull

ClauseBEこれが参照型(最も可能性の高いケース)であると仮定すると、これを単純化できObject.Equals()ます。無限再帰とスタックのパンクを回避するために使用していることに注意してください。

    // First test
    if (Object.Equals(a, null) && Object.Equals(b, null))
    {
        return true;
    }

便利なショートカットの1つはObject.ReferenceEquals()、nullを処理する--を使用することです。

したがって、代わりにこれを書くことができます:

    // First test
    if (Object.ReferenceEquals(a, b))
    {
        return true;
    }

aこれは、とbがまったく同じオブジェクトである場合も処理するというボーナスがあります。

Object.ReferenceEquals()テストを通過すると、それを知ってab違います。

だからあなたの次のテスト:

    // Second test
    if ((a as object == null && b as object != null)
        || (b as object == null && a as object != null))
    {
        return false;
    }

単純化することができます-がnullであることがわかっているので、anullにbすることはできません。

    // Second test
    if (Object.Equals(a, null) || Object.Equals(b, null))
    {
        return false;
    }

このテストが失敗した場合は、abが異なり、どちらもnullではないことがわかります。オーバーライドされたを呼び出す良い機会Equals()です。

    // Use the implementation of Equals() for the rest
    return a.Equals(b as object);
于 2009-06-10T03:27:28.687 に答える
0
public class Foo : IEquatable<Foo>
{
    public Int32 Id { get; set; }

    public override Int32 GetHashCode()
    {
        return this.Id.GetHashCode();
    }

    public override Boolean Equals(Object obj)
    {
        return !Object.ReferenceEquals(obj as Foo, null)
            && (this.Id == ((Foo)obj).Id);

        // Alternative casting to Object to use == operator.
        return ((Object)(obj as Foo) != null) && (this.Id == ((Foo)obj).Id);
    }

    public static Boolean operator ==(Foo a, Foo b)
    {
        return Object.Equals(a, b);
    }

    public static Boolean operator !=(Foo a, Foo b)
    {
        return !Object.Equals(a, b);
    }

    public Boolean Equals(Foo other)
    {
        return Object.Equals(this, other);
    }
}
于 2009-06-09T22:54:07.727 に答える
0

私は次のアプローチを使用しましたが、それは私にとってうまくいくようでした。実際、Resharperはこのアプローチを提案しています。

public bool Equals(Foo pFoo)
{
        if (pFoo == null)
            return false;
        return (pFoo.Id == Id);
}

public override bool Equals(object obj)
{
        if (ReferenceEquals(obj, this))
            return true;

        return Equals(obj as Foo);
}
于 2009-06-10T02:38:23.013 に答える
0

私は、Equals(T)メソッドですべての比較ロジックを実行し、「これまたはそれがnullの場合、それ以外の場合は...」をフレームワークへの演算子のオーバーロードに残すことを好みます。

演算子のオーバーロードをオーバーライドする際の唯一の注意点は、たとえば、と比較するために、Equals実装でこれらの演算子を使用できなくなることですnull。代わりにobject.ReferenceEquals、同じ効果を達成するために使用できます。

Equals()とOperator ==のオーバーライドに関するMSDNガイドラインのTwoDPointの例に従って、これは、型の値の同等性を実装するときに生成するパターンです。

public override bool Equals( object obj ) {
  // Note: For value types, would use:
  // return obj is TwoDPoint && this.Equals( (TwoDPoint)obj );
  return this.Equals( obj as TwoDPoint );
}

public bool Equals( TwoDPoint other ) {
  // Note: null check not needed for value types.
  return !object.ReferenceEquals( other, null )
      && EqualityComparer<int>.Default.Equals( this.X, other.X )
      && EqualityComparer<int>.Default.Equals( this.Y, other.Y );
}

public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
  // System.Collections.Generic.EqualityComparer<T> will perform the null checks 
  //  on the operands, and will call the Equals overload if necessary.
  return EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}

public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
  return !EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}

上記の形式は、フィールドの等価性チェックをフレームワークに転送するだけであり、フィールドが等価性演算子をオーバーロードするかどうかの知識を必要としないため、最も安全な実装です。過負荷が存在することがわかっている場合は、これを単純化することはまったく問題ありません。

public bool Equals( TwoDPoint other ) {
  return !object.ReferenceEquals( other, null )
      && this.X == other.X
      && this.Y == other.Y;
}

参照型を比較す​​るとき、またはボックス化された値の型が重要でない場合EqualityComparer<T>は、演算子のオーバーロードの呼び出しを静的メソッドの呼び出しに置き換えることもできます。object.Equals

public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
  return object.Equals( left, right );
}

public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
  return !object.Equals( left, right );
}

オーバーライドされたGetHashCodeに最適なアルゴリズムは何ですか?も参照してください。を実装するためGetHashCode

于 2009-07-19T14:08:01.543 に答える