IStructuralEquatable 및 IStructuralComparable은 어떤 문제를 해결합니까?
이 두 인터페이스와 여러 관련 클래스가 .NET 4에 추가되었습니다. 나는 그들에 대한 여러 블로그를 읽었지만 여전히 .NET 4 이전에 까다로운 문제를 해결했는지 알 수 없습니다.
사용은 무엇 IStructuralEquatable
과 IStructuralComparable
?
.NET의 모든 유형은 Object.Equals()
기본적으로 참조 동등성을 위해 두 유형을 비교 하는 메소드를 지원합니다 . 그러나 때로는 구조적 동등성을 위해 두 가지 유형을 비교할 수있는 것이 바람직합니다 .
가장 좋은 예는 .NET 4에서 IStructuralEquatable
인터페이스를 구현하는 배열 입니다. 이를 통해 두 배열을 참조 동등성 또는 "구조적 동등성"에 대해 비교하는지 여부 (각 위치에 동일한 값을 가진 항목 수가 동일한 지 여부)를 구별 할 수 있습니다. 예를 들면 다음과 같습니다.
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
구조적 동등성 / 비교 성을 구현하는 다른 유형에는 튜플 및 익명 유형이 포함됩니다. 두 유형 모두 구조와 내용을 기반으로 비교를 수행 할 수있는 기능의 이점이 있습니다.
당신이 묻지 않은 질문은 :
우리는 왜해야합니까
IStructuralComparable
하고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
다음은 두 인터페이스의 가능한 사용법을 보여주는 또 다른 예입니다.
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
IStructuralEquatable
인터페이스에 대한 설명 ( "설명"섹션에서) :
The
IStructuralEquatable
interface enables you to implement customized comparisons to check for the structural equality of collection objects.
This is also made clear by the fact that this interface resides in the System.Collections
namespace.
F# started using them since .net 4. ( .net 2 is here)
These interfaces are crucial to F#
let list1 = [1;5;9]
let list2 = List.append [1;5] [9]
printfn "are they equal? %b" (list1 = list2)
list1.GetType().GetInterfaces().Dump()
C# in a nutshell book:
Because Array is a class, arrays are always (themselves)
reference types
, regardless of the array’s element type. This means that the statementarrayB = arrayA
results in two variables that reference the same array. Similarly, two distinct arrays will always fail an equality test—unless you use a custom equality comparer. Framework 4.0 introduced one for the purpose of comparing elements in arrays which you can access via theStructuralComparisons
type.
object[] a1 = { "string", 123, true};
object[] a2 = { "string", 123, true};
Console.WriteLine(a1 == a2); // False
Console.WriteLine(a1.Equals(a2)); // False
IStructuralEquatable se1 = a1;
Console.WriteLine(se1.Equals(a2, StructuralComparisons.StructuralEqualityComparer)); // True
Console.WriteLine(StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)); // True
object[] a3 = {"string", 123, true};
object[] a4 = {"string", 123, true};
object[] a5 = {"string", 124, true};
IStructuralComparable se2 = a3;
Console.WriteLine(se2.CompareTo(a4, StructuralComparisons.StructuralComparer)); // 0
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a3, a4)); // 0
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a4, a5)); // -1
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a5, a4)); // 1
ReferenceURL : https://stackoverflow.com/questions/3609823/what-problem-does-istructuralequatable-and-istructuralcomparable-solve
'Programing' 카테고리의 다른 글
변수 값에서 접미사 #DEN은 무엇을 의미합니까? (0) | 2021.01.08 |
---|---|
계속하기 전에 여러 비동기 호출이 완료 될 때까지 대기 (0) | 2021.01.08 |
iOS 애플리케이션간에 키 체인 데이터를 공유하는 방법 (0) | 2021.01.08 |
동일한 프로젝트에 대해 동시에 여러 git 브랜치를 볼 수 있습니까? (0) | 2021.01.08 |
int main ()과 int main (void)의 차이점은 무엇입니까? (0) | 2021.01.08 |