無限再帰を行わずに「==」演算子のオーバーロードで null をチェックするにはどうすればよいですか?
-
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
そしてobjB
はnull
, 、メソッドは戻りますtrue
. - それは次のいずれかを決定します
objA
またはobjB
はnull
. 。そうであれば、それは戻ります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;
}
オブジェクト プロパティを使用して、結果として生じる 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;
}