次のような複雑なオブジェクトが2つありますObject1
そしてObject2
。子オブジェクトは約 5 レベルあります。
それらが同じかどうかを判断する最も速い方法が必要です。
これを C# 4.0 でどう実現できるでしょうか?
ベストアンサー1
埋め込むIEquatable<T>
(通常は継承されたObject.Equals
そしてObject.GetHashCode
すべてのカスタム型にメソッド(複数可)を呼び出します。複合型の場合は、Equals
包含型内で包含型のメソッドを呼び出します。包含コレクションの場合は、SequenceEqual
拡張メソッドは、各要素に対して内部的にIEquatable<T>.Equals
またはを呼び出しますObject.Equals
。このアプローチでは、明らかに型の定義を拡張する必要がありますが、その結果はシリアル化を伴う一般的なソリューションよりも高速になります。
編集: 3 段階のネストを持つ不自然な例を次に示します。
値型の場合、通常はメソッドを呼び出すだけですEquals
。フィールドまたはプロパティが明示的に割り当てられていない場合でも、デフォルト値が保持されます。
参照型の場合は、まずReferenceEquals
は参照の等価性をチェックします。これは、同じオブジェクトを参照している場合に効率を高めるのに役立ちます。また、両方の参照が null の場合も処理します。そのチェックが失敗した場合は、インスタンスのフィールドまたはプロパティが null でないことを確認し ( を回避するためNullReferenceException
)、そのEquals
メソッドを呼び出します。メンバーは適切に型指定されているため、オーバーライドされたメソッド (型キャストのため実行がわずかに遅くなります)IEquatable<T>.Equals
をバイパスして、メソッドが直接呼び出されます。Object.Equals
をオーバーライドする場合Object.Equals
、 もオーバーライドする必要がありますObject.GetHashCode
が、簡潔にするために以下ではオーバーライドしていません。
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
アップデート: この回答は数年前に書かれたものです。それ以来、私はそのようなシナリオでは可変型の実装を避けるようになりましたIEquality<T>
。等価性には 2 つの概念があります。身元そして等価メモリ表現レベルでは、これらは一般に「参照の等価性」と「値の等価性」として区別されます(平等の比較)。しかし、同じ区別はドメインレベルにも当てはまります。クラスに、Person
現実PersonId
世界の異なる人物ごとに固有のプロパティがあるとします。同じだPersonId
が値が異なる2つのオブジェクトAge
は、等しいと見なすべきでしょうか、それとも異なると見なすべきでしょうか。上記の回答は、等価性の後にあると仮定しています。しかし、IEquality<T>
コレクションなど、インターフェースの多くの使用法では、そのような実装が以下を提供することを前提としています。身元たとえば、 を入力する場合HashSet<T>
、通常はTryGetValue(T,T)
呼び出しは、単に引数の同一性を共有する既存の要素を返すものであり、必ずしも内容が完全に同じである同等の要素を返すものではありません。この概念は、GetHashCode
:
一般に、可変参照型の場合は、
GetHashCode()
次の場合にのみオーバーライドする必要があります。
- 変更不可能なフィールドからハッシュコードを計算できます。
- ハッシュ コードに依存するコレクションにオブジェクトが含まれている間は、可変オブジェクトのハッシュ コードが変更されないようにすることができます。