無限再帰を行わずに「==」演算子のオーバーロードで null をチェックするにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/73713

  •  09-06-2019
  •  | 
  •  

質問

以下では、== 演算子のオーバーロード メソッドで無限再帰が発生します。

    Foo foo1 = null;
    Foo foo2 = new Foo();
    Assert.IsFalse(foo1 == foo2);

    public static bool operator ==(Foo foo1, Foo foo2) {
        if (foo1 == null) return foo2 == null;
        return foo1.Equals(foo2);
    }

Null をチェックするにはどうすればよいですか?

役に立ちましたか?

解決

使用 ReferenceEquals:

Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);

public static bool operator ==(Foo foo1, Foo foo2) {
    if (object.ReferenceEquals(null, foo1))
        return object.ReferenceEquals(null, foo2);
    return foo1.Equals(foo2);
}

他のヒント

オーバーロード メソッドでオブジェクトにキャストします。

public static bool operator ==(Foo foo1, Foo foo2) {
    if ((object) foo1 == null) return (object) foo2 == null;
    return foo1.Equals(foo2);
}

使用 参照が等しい. 。から MSDN フォーラム:

public static bool operator ==(Foo foo1, Foo foo2) {
    if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null);
    if (ReferenceEquals(foo2, null)) return false;
    return foo1.field1 == foo2.field2;
}

試す Object.ReferenceEquals(foo1, null)

とにかく過負荷はお勧めしません ==オペレーター;参照を比較するために使用する必要があります。 Equals 「意味論的な」比較の場合。

オーバーライドした場合 bool Equals(object obj) そしてオペレーターが欲しい == そして Foo.Equals(object obj) 同じ答えを返すには、通常、 != このような演算子:

public static bool operator ==(Foo foo1, Foo foo2) {
  return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
  return !object.Equals(foo1, foo2);
}

オペレーター == すべての null チェックを行った後、最終的に呼び出します foo1.Equals(foo2) 2 つが等しいかどうか実際のチェックを行うためにオーバーライドしました。

C# 7 以降を使用している場合は、null 定数パターン マッチングを使用できます。

public static bool operator==(Foo foo1, Foo foo2)
{
    if (foo1 is null)
        return foo2 is null;
    return foo1.Equals(foo2);
}

これにより、object.ReferenceEquals(foo1, null) を呼び出すコードよりもわずかに整ったコードが得られます。

私のアプローチは次のとおりです

(object)item == null

私が頼りにしているもの objectは間違いのない独自の等価演算子です。または、カスタム拡張メソッド (およびオーバーロード):

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null;
}

public static bool IsNull<T>(this T? obj) where T : struct
{
    return !obj.HasValue;
}

または、より多くのケースを処理するには、次のようにすることもできます。

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null || obj == DBNull.Value;
}

制約により、 IsNull 値の型について。今では電話するのと同じくらい楽しいです

object obj = new object();
Guid? guid = null; 
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error

つまり、一貫した、エラーが発生しにくいスタイルで null をチェックすることができます。私も見つけました (object)item == null よりも非常に非常にわずかに速いです Object.ReferenceEquals(item, null), ただし、それが重要な場合に限ります (私は現在、すべてをマイクロ最適化する必要があることに取り組んでいます!)。

等価性チェックの実装に関する完全なガイドを参照するには、次を参照してください。 参照型の 2 つのインスタンスを比較するための「ベスト プラクティス」とは何ですか?

静電気 Equals(Object, Object) 方法 2 つのオブジェクトかどうかを示します。 objA そして objB, 、 は同じ。また、値が次のオブジェクトをテストすることもできます。 null 平等のために。比較する objA そして objB 平等の場合は次のようになります。

  • 2 つのオブジェクトが同じオブジェクト参照を表すかどうかを判断します。存在する場合、メソッドは戻ります。 true. 。このテストは、 ReferenceEquals 方法。さらに、両方の場合は、 objA そして objBnull, 、メソッドは戻ります true.
  • それは次のいずれかを決定します objA または objBnull. 。そうであれば、それは戻ります false。2 つのオブジェクトが同じオブジェクト参照を表しておらず、どちらも同じでない場合 null, 、それは呼び出します objA.Equals(objB) そして結果を返します。これは、次のことを意味します。 objA をオーバーライドします Object.Equals(Object) メソッドでこのオーバーライドが呼び出されます。

.

public static bool operator ==(Foo objA, Foo objB) {
    return Object.Equals(objA, objB);
}

さらに返信する オーバーライド演算子 null との比較方法 ここに重複としてリダイレクトされます。

値オブジェクトをサポートするためにこれが行われている場合、私は新しい表記法が便利であると感じており、比較が行われる場所が 1 か所だけであることを確認したいと考えています。また、Object.Equals(A, B) を利用すると、null チェックが簡素化されます。

これにより、==、!=、Equals、および GetHashCode がオーバーロードされます。

    public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
    public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
    public override bool Equals(object other) => Equals(other as ValueObject );
    public bool Equals(ValueObject other) {
        return !(other is null) && 
               // Value comparisons
               _value == other._value;
    }
    public override int GetHashCode() => _value.GetHashCode();

より複雑なオブジェクトの場合は、Equals での比較とより豊富な GetHashCode を追加します。

実はもっと簡単にチェックする方法があります null この場合:

if (foo is null)

それでおしまい!

この機能は C# 7 で導入されました

演算子 == のオーバーロードでよくあるエラーは、 (a == b), (a ==null), 、 または (b == null) 参照が等しいかどうかを確認します。代わりにこれ 結果として オーバーロードされた演算子 == の呼び出しにより、 infinite loop. 。使用 ReferenceEquals または、ループを避けるために、タイプをオブジェクトにキャストします。

これをチェックしてください

// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
    return true;
}

// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
    return false;
}

参照 Equals() および Operator == のオーバーロードに関するガイドライン

オブジェクト プロパティを使用して、結果として生じる NullReferenceException をキャッチすることができます。試行するプロパティが Object から継承またはオーバーライドされている場合、これはどのクラスでも機能します。

public static bool operator ==(Foo foo1, Foo foo2)
{
    //  check if the left parameter is null
    bool LeftNull = false;
    try { Type temp = a_left.GetType(); }
    catch { LeftNull = true; }

    //  check if the right parameter is null
    bool RightNull = false;
    try { Type temp = a_right.GetType(); }
    catch { RightNull = true; }

    //  null checking results
    if (LeftNull && RightNull) return true;
    else if (LeftNull || RightNull) return false;
    else return foo1.field1 == foo2.field2;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top