これら 2 つのインターフェイスと、関連するいくつかのクラスが .NET 4 に追加されていることに気付きました。それらに関するいくつかのブログを読んだことがありますが、.NET 4 より前では困難だった、どのような問題を解決するのかまだわかりません。
IStructuralEquatable
とは何に使うのIStructuralComparable
ですか?
これら 2 つのインターフェイスと、関連するいくつかのクラスが .NET 4 に追加されていることに気付きました。それらに関するいくつかのブログを読んだことがありますが、.NET 4 より前では困難だった、どのような問題を解決するのかまだわかりません。
IStructuralEquatable
とは何に使うのIStructuralComparable
ですか?
.NET のすべての型はObject.Equals()
、既定で 2 つの型を比較して参照が等しいかどうかを調べるメソッドをサポートしています。ただし、場合によっては、2 つのタイプの構造上の同等性を比較できることが望ましい場合もあります。
これの最も良い例は配列で、.NET 4 でIStructuralEquatable
インターフェイスを実装するようになりました。これにより、2 つの配列を比較して参照が等しいか、「構造が等しい」かを区別できます。つまり、各位置に同じ値を持つ同じ数の項目があるかどうかです。次に例を示します。
int[] array1 = new int[] { 1, 5, 9 };
int[] array2 = new int[] { 1, 5, 9 };
// using reference comparison...
Console.WriteLine( array1.Equals( array2 ) ); // outputs false
// now using the System.Array implementation of IStructuralEquatable
Console.WriteLine(
StructuralComparisons.StructuralEqualityComparer.Equals( array1, array2 )
); // outputs true
構造的等価性/比較可能性を実装するその他の型には、タプルと匿名型が含まれます。どちらも、構造と内容に基づいて比較を実行できるという利点があります。
あなたが尋ねていない質問は次のとおりです。
andインターフェイスが既に存在するのに、なぜ
IStructuralComparable
andがあるのでしょうか。IStructuralEquatable
IComparable
IEquatable
私が提供する答えは、一般に、参照比較と構造比較を区別することが望ましいということです。通常、実装IEquatable<T>.Equals
する場合は、オーバーライドObject.Equals
して一貫性を保つことも期待されます。この場合、どのように参照と構造の同等性をサポートしますか?
同じ質問がありました。LBushkin の例を実行したとき、別の答えが得られたことに驚きました。その答えには8つの賛成票がありますが、間違っています。多くの「リフレクター」の後、これが私の見解です。
特定のコンテナー (配列、タプル、匿名型) はIStructuralComparable
、 およびIStructuralEquatable
.
IStructuralComparable
深いデフォルトの並べ替えをサポートします。IStructuralEquatable
深いデフォルトのハッシュをサポートします。{EqualityComparer<T>
浅い (1 つのコンテナー レベルのみ)、デフォルトのハッシュをサポートすることに注意してください。}
私が見る限り、これは StructuralComparisons クラスを通じてのみ公開されます。これを便利にする唯一の方法は、次のStructuralEqualityComparer<T>
ようにヘルパー クラスを作成することです。
public class StructuralEqualityComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return StructuralComparisons.StructuralEqualityComparer.Equals(x,y);
}
public int GetHashCode(T obj)
{
return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
}
private static StructuralEqualityComparer<T> defaultComparer;
public static StructuralEqualityComparer<T> Default
{
get
{
StructuralEqualityComparer<T> comparer = defaultComparer;
if (comparer == null)
{
comparer = new StructuralEqualityComparer<T>();
defaultComparer = comparer;
}
return comparer;
}
}
}
これで、コンテナ内のコンテナ内にコンテナを持つアイテムで HashSet を作成できます。
var item1 = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
var item1Clone = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
var item2 = Tuple.Create(1, new int[][] { new int[] { 1, 3 }, new int[] { 3 } });
var set = new HashSet<Tuple<int, int[][]>>(StructuralEqualityComparer<Tuple<int, int[][]>>.Default);
Console.WriteLine(set.Add(item1)); //true
Console.WriteLine(set.Add(item1Clone)); //false
Console.WriteLine(set.Add(item2)); //true
これらのインターフェースを実装することで、独自のコンテナを他のコンテナとうまく連携させることもできます。
public class StructuralLinkedList<T> : LinkedList<T>, IStructuralEquatable
{
public bool Equals(object other, IEqualityComparer comparer)
{
if (other == null)
return false;
StructuralLinkedList<T> otherList = other as StructuralLinkedList<T>;
if (otherList == null)
return false;
using( var thisItem = this.GetEnumerator() )
using (var otherItem = otherList.GetEnumerator())
{
while (true)
{
bool thisDone = !thisItem.MoveNext();
bool otherDone = !otherItem.MoveNext();
if (thisDone && otherDone)
break;
if (thisDone || otherDone)
return false;
if (!comparer.Equals(thisItem.Current, otherItem.Current))
return false;
}
}
return true;
}
public int GetHashCode(IEqualityComparer comparer)
{
var result = 0;
foreach (var item in this)
result = result * 31 + comparer.GetHashCode(item);
return result;
}
public void Add(T item)
{
this.AddLast(item);
}
}
HashSet
これで、コンテナ内のカスタム コンテナ内にコンテナを持つアイテムで を作成できます。
var item1 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
var item1Clone = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
var item2 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 3 }, new int[] { 3 } });
var set = new HashSet<Tuple<int, StructuralLinkedList<int[]>>>(StructuralEqualityComparer<Tuple<int, StructuralLinkedList<int[]>>>.Default);
Console.WriteLine(set.Add(item1)); //true
Console.WriteLine(set.Add(item1Clone)); //false
Console.WriteLine(set.Add(item2)); //true
次に、2 つのインターフェイスの可能な使用法を示す別の例を示します。
var a1 = new[] { 1, 33, 376, 4};
var a2 = new[] { 1, 33, 376, 4 };
var a3 = new[] { 2, 366, 12, 12};
Debug.WriteLine(a1.Equals(a2)); // False
Debug.WriteLine(StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)); // True
Debug.WriteLine(StructuralComparisons.StructuralComparer.Compare(a1, a2)); // 0
Debug.WriteLine(StructuralComparisons.StructuralComparer.Compare(a1, a3)); // -1