반응형
요약 : 직접 만든 클래스를 Dictionary의 key로 사용하고 싶을 경우 추가적으로 해야 할 작업을 알아본다.
1. 아래와 같이 Point class를 만들고 좌표가 같을 경우 같은 Point로 인식하게 하고 싶다.
public class Point
{
public Point(int x, int y)
{
X = x;
Y = y;
}
public int X { get; set; }
public int Y { get; set; }
}
현재 상태에서 좌표가 같은 두 Point를 Dictionary에 Add()하면 예외 없이 정상적으로 추가된다.
var p1 = new Point(0, 0);
var p2 = new Point(0, 0);
var points = new Dictionary<Point, int>();
points.Add(p1, 0);
points.Add(p2, 1);
3. 좌표가 같은 Point를 동일한 Point로 인식하게 하기 위해 Dictionary의 Add()에서 key를 어떤 식으로 체크하는지 살펴보았다.
Add()는 내부적으로 Insert()를 호출하고
public void Add(TKey key, TValue value) {
Insert(key, value, true);
}
Insert()는 해시값과 Equals()를 이용해 모두 true일 경우 중복 키 삽입으로 판단하고 있다.
private void Insert(TKey key, TValue value, bool add) {
...
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
...
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
...
ref : github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/dictionary.cs
4. 다시 말해 좌표가 같을 경우 같은 Point로 인식하게 하려면 Equals()와 GetHashCode()를 x, y를 사용해 적절히 override 해주면 된다.
public class Point
{
public Point(int x, int y)
{
X = x;
Y = y;
}
public int X { get; set; }
public int Y { get; set; }
private bool Equals(Point point)
{
return point.X.Equals(X) && point.Y.Equals(Y);
}
public override bool Equals(object obj)
{
return this.Equals(obj as Point);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
5. override후 좌표가 같은 Point 추가 시 System.ArgumentException: An item with the same key has already been added 예외가 발생하는 것을 볼 수 있다.
var p1 = new Point(0, 0);
var p2 = new Point(0, 0);
var points = new Dictionary<Point, int>();
Assert.ThrowsException<ArgumentException>(() =>
{
points.Add(p1, 0);
points.Add(p2, 0);
});
반응형