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


113

以下は==演算子オーバーロードメソッドで無限再帰を引き起こします

    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を確認するにはどうすればよいですか?

回答:


138

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

このソリューションは機能しませんAssert.IsFalse(foo2 == foo1);
FIL

そしてfoo1.Equals(foo2)、たとえば、私が必要なfoo1 == foo2場合のみ、どういう意味foo1.x == foo2.x && foo1.y == foo2.yですか?この応答は、場合を無視していないですfoo1 != nullけどfoo2 == null
ダニエル

注:構文がより単純な同じソリューション:if (foo1 is null) return foo2 is null;
レム

20

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

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

1
丁度。どちら(object)foo1 == nullfoo1 == (object)nullに行きます内蔵の過負荷==(object, object)ではなく、ユーザー定義の過負荷に==(Foo, Foo)。それはメソッドのオーバーロード解決のようなものです。
Jeppe Stig Nielsen 2014

2
将来の訪問者にとって-受け入れられた答えは、==オブジェクトを実行する関数です。これは基本的に受け入れられた回答と同じですが、欠点が1つあります。キャストが必要です。したがって、有罪判決の答えは優れています。
マフィイ

1
@Mafiiキャストは純粋にコンパイル時の操作です。コンパイラはキャストが失敗しないことを知っているので、実行時に何もチェックする必要はありません。方法の違いは完全に美的です。
サービー


4

試す Object.ReferenceEquals(foo1, null)

とにかく、==演算子をオーバーロードすることはお勧めしません。参照の比較、およびEquals「セマンティック」比較に使用する必要があります。


4

オーバーライド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つが等しいかどうかを実際にチェックするように呼び出します。


これは非常に適切だと感じています。とObject.Equals(Object, Object)並べての実装を見ると、他の回答で提案されているすべてのObject.ReferenceEquals(Object, Object)ことをすぐに実行できることObject.Equals(Object, Object)は明らかです。使ってみませんか?
2015年

@tne ==必要なのがデフォルトの動作である場合は、演算子をオーバーロードする意味がないからです。カスタム比較ロジックを実装する必要がある場合にのみ、オーバーロードする必要があります。つまり、参照等価チェック以上のものです。
Dan Bechard

@ダンあなたは私の発言を誤解していると確信しています。オーバーロード==が望ましいことがすでに確立されているコンテキストでは(質問はそれを意味します)Object.Equals(Object, Object)使用ReferenceEqualsまたは明示的なキャストのような他のトリックを不要にすることでこの答えをサポートしています(したがって、「なぜそれを使用しないのですか?」、「それ」であることEquals(Object, Object))。無関係なあなたのポイントも正しいとしても、私はさらに進んでいき==ます。「値オブジェクト」として分類できるのはオブジェクトのオーバーロードのみです。
2015年

@tne主な違いは、Fooがオーバーライドする可能性が高い仮想メソッドであるObject.Equals(Object)Object.Equals(Object, Object)順に呼び出すことです。等価性チェックに仮想呼び出しを導入したという事実は、これらの呼び出しを最適化する(インラインなど)コンパイラの機能に影響を与える可能性があります。これはおそらくほとんどの目的では無視できますが、場合によっては、等価演算子の小さなコストがループやソートされたデータ構造の大きなコストを意味することがあります。
Dan Bechard、2015年

仮想メソッド呼び出しの最適化の複雑さの詳細について@tneは、参照stackoverflow.com/questions/530799/...
Dan Bechard、2015年

3

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

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

これにより、呼び出し元のオブジェクトよりも少し簡潔なコードが得られます。


2
またはpublic static bool operator==( Foo foo1, Foo foo2 ) => foo1?.Equals( foo2 ) ?? foo2 is null;
DankoDurbić18年

3

nullこの場合、実際にチェックする簡単な方法があります。

if (foo is null)

それでおしまい!

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


1

私のアプローチは

(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つのインスタンスを比較するための「ベストプラクティス」とは何ですか?を参照してください。


Nitpick:読者はDbNull、比較などの機能にジャンプする前に、依存関係を監視する必要があります。IMOを使用しても、SRPに関連する問題発生しないケースは非常にまれです。ただし、コードの臭いを指摘するだけで、非常に適切な場合があります。
2015年

0

静的Equals(Object, Object)メソッドは、2つのオブジェクトobjAobjBが等しいかどうかを示します。また、値がnull同等であるオブジェクトをテストすることもできます。次のように比較objAobjB、同等であることを確認します。

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

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

0

ここで重複としてリダイレクトする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を追加します。


0

モダンで凝縮された構文の場合:

public static bool operator ==(Foo x, Foo y)
{
    return x is null ? y is null : x.Equals(y);
}

public static bool operator !=(Foo x, Foo y)
{
    return x is null ? !(y is null) : !x.Equals(y);
}

-3

オペレータのオーバーロードで一般的なエラーは==使用することで(a == b)(a ==null)または(b == null)参照の等価性をチェックします。代わり、これ によりオーバーロードされた演算子==が呼び出され、が発生しinfinite loopます。ReferenceEqualsループを回避するには、タイプをObjectに使用またはキャストします。

これをチェック

// 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()および演算子==のオーバーロードのリファレンス ガイドライン


1
この情報のすべてですでに複数の回答があります。同じ答えの7番目のコピーは必要ありません。
16年

-5

オブジェクトプロパティを使用して、結果の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;
}

nullオブジェクトが多い場合、例外処理は大きなオーバーヘッドになる可能性があります。
Kasprzol 2008

2
はは、私はこれが最善の方法ではないことに同意します。このメソッドを投稿した後、すぐに現在のプロジェクトを修正して、代わりにReferenceEqualsを使用しました。ただし、最適ではないにも関わらず機能するため、この質問に対する有効な回答です。
Digital Gabeg 2008
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.