2012-02-13 4 views
3

본인에게는 외부의 물리적 측정 장치를 나타내는 클래스가 있습니다. 단순화 된 버전은 다음과 같다 :IEquatable 구현 변경 가능 유형

public class Device { 
    public string Tag { get; set; } 
    public int Address { get; set; } 
} 

Tag는 디바이스를 식별하는 사용자 정의 값이다. Address은 어댑터가 장치와 통신하는 데 사용하는 값입니다. Device의 두 인스턴스가 동일한 Address 인 경우 동일한 외부 측정 장치가 사용됩니다.

내가 코드에서 IEquatable<T>Equals를 재정 의하여 및 구현 (ContainsDistinct 같은 방법을 사용하는) 그 행동을 모방하고 싶습니다 : 당신이, 내가 Tag 속성을 무시하고있어 볼 수 있듯이

public class Device : IEquatable<Device> { 
    public string Tag { get; set; } 
    public int Address { get; set; } 

    public override bool Equals(object obj) { 
     return Equals(obj as Device); 
    } 
    public bool Equals(Device other) { 
     if (null == other) return false; 
     if (ReferenceEquals(this, other)) return true; 
     return Address.Equals(other.Address); 
    } 
} 

Equals의 구현.

그래서, 내 질문은 : 내가 Equals의 구현에 Tag 속성을 무시해야 하는가? 그렇게하면 코드를 이해하기가 더 어려워 집니까? 내가하려는 일을하는 더 좋은 방법이 있습니까? Tag 속성이 필요합니다. 종종 사용자가 Address을 모르거나 DeviceAddress이 있는지 여부 (App.config 파일에서 처리되므로 사용자는 IDevice에는 Address 속성이 없음).

업데이트 : 응답

감사합니다 여러분.

그래서 나는 사용자 정의 IEqualityComparer을 사용해야한다는 것을 모았습니다. 내 실제 코드가 더 비슷해 보이는 경우 그렇게하는 방법에 대한 지침이 있습니까?

public interface IDevice { 
    string Tag { get; set; } 
    double TakeMeasurement(); 
} 
internal class Device : IDevice { 
    public string Tag { get; set; } 
    public int Address { get; set; } 
    public double TakeMeasurement() { 
     // Take a measurement at the device's address... 
    } 
} 

기기 유형을 내 IEqualityComparer으로 확인해야합니까?

public class DeviceEqualityComparer : IEqualityComparer<IDevice> { 
    public bool Equals(IDevice x, IDevice y) { 
     Contract.Requires(x != null); 
     Contract.Requires(y != null); 
     if ((x is Device) && (y is Device)) { 
      return x.Address.Equals(y.Address); 
     } 
     else { 
      return x.Equals(y); 
     } 
    } 

    public int GetHashCode(IDevice obj) { 
     Contract.Requires(obj != null); 
     if (obj is Device) { 
      return obj.Address.GetHashCode(); 
     } 
     else { 
      return obj.GetHashCode(); 
     } 
    } 
} 
+1

후속 질문이있는 경우 별도로 질문해야합니다. 이 방법을 사용하면 서로 다른 두 가지 질문을 통해 어느 부분이 응답되고 있는지에 대한 혼란없이 좋은 답변을 얻을 수 있습니다.물론 일부 문맥에 대해 이전 질문에 대한 참조를 넣을 수 있습니다 (그리고 새 질문에 관련 비트를 복사 할 수 있습니다). – Chris

답변

2

예, 현재 구현은 확실히 혼란이다. 당신이 정의한 평등은 분명히 장치에 대한 평등의 올바른 개념이 아닙니다. 당신이했던 것처럼

그래서, 오히려 IEquatable<Device>를 구현하는 것보다, 나는 당신이 Contains, DistinctIEqualityComparer<T>의 인스턴스를 통과 할 수 어쩌면,

class DeviceAddressEqualityComparer : IEqualityComparer<Device> { 
    public bool Equals(Device x, Device y) { 
     Contract.Requires(x != null); 
     Contract.Requires(y != null); 
     return x.Address.Equals(y.Address); 
    } 

    public int GetHashCode(Device obj) { 
     Contract.Requires(obj != null); 
     return obj.Address.GetHashCode(); 
    } 
} 

IEqualityComparer<Device>의 구현을 정의하는 것에 종속 된 다른 LINQ 방법 평등 (예 : GroupBy).

+0

감사합니다. 그건 의미가 있습니다. 그러나 장치 유형은 내 어셈블리 내부에 있습니다. 내 질문을 다른 구현으로 업데이트했습니다. 그것은 의미가 있습니까? – ken

1

평등을 Tag과 같이 구현해야합니까? 그것은 당신처럼하는 소리가 아니므로, 나는 당신의 접근 방식에 문제가있는 것을 보지 못합니다.

사용자가 Address에 대해 알 필요가없는 경우 주소를 기반으로하는 기본 평등에 대해 알 필요가 없다고 주장 할 수도 있습니다 ... 그렇습니까? 그들이 그렇지 않다면, 나는 다시 당신의 접근 방식에 문제가 없다고 말할 것입니다.

평등에 대해 알아야 할 경우 디자인을 다시 생각하고 Address을 어떤 식 으로든 노출해야 할 수도 있습니다.

+0

아니요, 주소를 기반으로하는 기본 평등에 대해 알 필요가 없습니다. 귀하의 의견에 동의하지 않는 다른 답변에 대한 의견이 있으십니까? 감사. – ken

+0

'IEqualityComparer '에 대해서는 .NET에 대해 충분히 알지 못한다고 인정합니다. 'GetHashCode'에 관해서는 사실이지만 이미 다뤘습니다. 서로 다른 태그를 가진 두 장치가'HashSet '(등)에서 하나가되어 혼동을 줄 수 있다는 의견에 관해서는 태그가 의미 상으로 다른 것으로 가정하고 있다고 생각합니다. 모든 필드는 평등을 위해 동일해야하며, 나는 그 가정을 거부합니다. 나는 네가 필요한 것을 가장 잘 안다고 생각한다. 그러나 혼동을 피하기 위해 매우 신중하게 문서화해야 할 수도 있습니다. –

4

먼저 GetHashCode()을 무시한 것을 잊어 버려 코드가 손상되었습니다.

IMO 두 개체가 모든 용도로 동등한 경우에만 Equals을 덮어 써야합니다. Tag이 다른 개체는 용도에 따라 다르게 보입니다.

나는이 객체들에 대해 Equals을 무시하지 않을 것입니다. 차라리 맞춤 비교 자 IEqualityComparer<T>을 구현하고 해당 위치를 사용하십시오. 평등이라는 개념을 가진 대부분의 메서드는 선택적 매개 변수로 IEqualityComparer<T>을 사용합니다.

나는 null 매개 변수를 금지하지 않지만이를 처리합니다. 나는 또한 참조 평등을위한 조기 검토를 추가했다.

public class DeviceByAddressEqualityComparer : IEqualityComparer<IDevice> { 
    public bool Equals(IDevice x, IDevice y) { 
     if(x==y) 
      return true; 
     if(x==null||y==null) 
      return false; 
     return x.Address.Equals(y.Address); 
    } 

    public int GetHashCode(IDevice obj) { 
     if(obj == null) 
      return 0; 
     else 
      return obj.Address.GetHashCode(); 
    } 
} 

문맥에 따라 유형을 확인하려는 경우. Equals을 재정의 할 때 나는 보통 x.GetType()==y.GetType() 일지를 검사하지만, 여기서 의도적으로 객체의 일부를 무시하는 특수 목적 비교자를 사용하고 있기 때문에 아마도 그 유형의 일부를 ID로 만들지 않을 것입니다.

+0

GetHashCode를 재정의했습니다. 예제에서 벗어났습니다. 응답 해 주셔서 감사합니다. – ken

+0

또한 내 질문을 업데이트했습니다. '장치'유형이 내 어셈블리 내부에 있습니다. IEqualityComparer 구현에 대한 의견이 있습니까? ? – ken

+0

@ken은'IEqualityComparer ' – CodesInChaos

2

Equals 구현에서 Tag 속성을 무시해야합니까?

아니요, 이것은 좋지 않은 생각입니다.

이렇게하면 코드를 이해하기가 어려워 집니까?

물론 새로운 개발자는 해시 세트에 다른 태그가있는 두 개의 장치가 하나의 장치가되는 것을 이해하지 못합니다.

내가하려는 일을하는 더 좋은 방법이 있습니까?

  • DeviceWithTag라는 클래스를 추가 사용자 정의 비교
  • 를 제공하고 Device "태그가 필요없는"을 유지 :

내가 생각할 수있는 적어도 두 가지 방법이 있습니다. 그것은 당신의 Tag 모델처럼 표시 목적 이외의 태그를 무시 로케이터 장치에 붙어 실제 "태그"를 보면 않기 때문에

나는, 두 번째 방법을 선호 할 것입니다.

0

IDevice을 구현할 새 인터페이스가 만들어졌습니다. 새로운 인터페이스를 사용하면 장치를 기반으로하는 동등 비교자를 쉽게 만들 수 있습니다.

public interface IPhysicallyEquatable<T> 
{ 
    bool PhysicallyEquals(T other); 
    int GetPhysicalHashCode(); 
} 

public class PhysicalEqualityComparer<T> : IEqualityComparer<T> 
    where T : IPhysicallyEquatable<T> 
{ 
    public bool Equals(T x, T y) 
    { 
     if (null == x) throw new ArgumentNullException("x"); 
     if (null == y) throw new ArgumentNullException("y"); 
     return x.PhysicallyEquals(y); 
    } 
    public int GetHashCode(T obj) 
    { 
     if (null == obj) throw new ArgumentNullException("obj"); 
     return obj.GetPhysicalHashCode(); 
    } 
} 

public interface IDevice : IPhysicallyEquatable<IDevice> 
{ 
    // ... 
}