2009-11-02 3 views
1

C#에서 동일한 추상 클래스에서 파생 된 두 클래스를 비교할 수 있기를 바랍니다. 다음 코드는 내 문제를 설명합니다.기본 클래스를 기반으로 두 개체를 어떻게 비교합니까?

이제 BaseClass을 추상화하지 않고 코드를 수정 한 다음 ToBassClass()new BaseClass 개체를 반환 할 수 있습니다. 그러나 더 우아하고 효율적인 솔루션이 아닌가?

abstract class BaseClass 
{ 
    BaseClass(int x) 
    { 
     X = x; 
    } 

    int X { get; private set; } 

    // It is probably not necessary to override Equals for such a simple class, 
    // but I've done it to illustrate my point. 
    override Equals(object other) 
    { 
     if (!other is BaseClass) 
     { 
      return false; 
     } 

     BaseClass otherBaseClass = (BaseClass)other; 

     return (otherBaseClass.X == this.X); 
    } 

    BaseClass ToBaseClass() 
    { 
     // The explicit is only included for clarity. 
     return (BaseClass)this; 
    } 
} 

class ClassA : BaseClass 
{ 
    ClassA(int x, int y) 
     : base (x) 
    { 
     Y = y; 
    } 

    int Y { get; private set; } 
} 

class ClassB : BaseClass 
{ 
    ClassB(int x, int z) 
     : base (x) 
    { 
     Z = z; 
    } 

    int Z { get; private set; } 
} 

var a = new A(1, 2); 
var b = new B(1, 3); 

// This fails because despite the call to ToBaseClass(), a and b are treated 
// as ClassA and ClassB classes so the overridden Equals() is never called. 
Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass()); 
+3

Assert.True()? – SoftMemes

+1

또한 int Z {get; 사립 Z; } int 여야합니다. Z {get; 개인 집합; }? – GraemeF

답변

2

에 따라 달라집니다. 정확히 일치하는지 테스트하고 싶습니다. 분명히 ClassAClassB 인스턴스는 그 단어의 진정한 의미에서 "동등"하지 않으므로 Equals을 무시하면 코드에 이상한 버그가 실제로 발생할 수 있습니다.

그러나 당신이 특정 기준에 따라 그들을 비교하고 싶은 경우에, 당신은 당신의 필요를 제품군 특정 IEqualityComparer (또는 여러 comparers)를 구현할 수 있습니다.

그래서,이 경우에, 당신은 것입니다 : 이것은 Equals 메서드를 재정의와 아무것도하지 않는 것을

참고 : 코멘트에 대해서는

/// <Summary> 
/// Compares two classes based only on the value of their X property. 
/// </Summary> 
public class ComparerByX : IEqualityComparer<BaseClass> 
{ 
    #region IEqualityComparer<BaseClass> Members 

    public bool Equals(BaseClass a, BaseClass b) 
    { 
     return (a.X == b.X); 
    } 

    public int GetHashCode(BaseClass obj) 
    { 
     return obj.X.GetHashCode(); 
    } 

    #endregion 

} 

[편집].

그러나이 같은 어떤지를 확인 할 수있을 것입니다 :

IEqualityComparer<BaseClass> comparer = new ComparerByX(); 
Assert.True(comparer.Equals(a, b)); 

이 처음에는 대단한 일처럼 보이지 않을 수도 있지만, 그것은 당신에게 몇 가지 장점이 있습니다 :

A) 당신을 원하는만큼 많은 IEqualityComparer<T> 개의 구현을 가질 수 있습니다. 경우에 따라 Equals 오버라이드가 너무 좋지 않다는 것을 알 수 있습니다. 그렇다면이 코드에 따라 모든 코드가 손상 될 위험이 있습니다.

B) 항목을 비교 IEqualityComparer<T>을 사용하여 실제로 많은 클래스가 있습니다.

예를 들어 BaseClass을 사전의 키로 사용할 수 있습니다. 이 경우, 당신은 IEqualityComparer<T> 받아들이는 Dictionary<Key,Value> 생성자 오버로드를 사용합니다 :

Dictionary<BaseClass, SomeOtherClass> dictionary 
    = new Dictionary<BaseClass, SomeOtherClass>(new ComparerByX()); 

이 방법을 사전 키 조회시 사용자 정의 ComparerByX을 사용합니다.

또한 예를 들어 LINQ를 사용하는 경우 Distinct() 메서드 예제를 확인할 수 있습니다. 또한 별개의 값을 반환하지만 지정된 사용자 지정 IEqualityComparer을 사용하여 비교되는 오버로드를 지원합니다.

+0

이렇게하면 .BaseClass() == b.ToBaseClass()가되지만 여전히! = b? –

2

해방는 지적 음, 여기 Assert.True를 사용하는 이상한 조금 있어요 - 무슨 뜻 않았다 Assert.AreEqual? 그렇다면 테스트 프레임 워크에 따라 다르긴하지만 (심지어 ToBaseClass 호출 없이도)이 기능이 작동 할 것으로 예상됩니다.

비록 상속에 관해서는 평등하다. 개인적으로, 나는 "객체의 특정 측면을 테스트 할 것"이라는 명시 적으로 적절한 IEqualityComparer<BaseClass>을 만들 것입니다. 즉, 상속이 기본적으로 관련되지 않는다는 것을 의미합니다.

2

먼저 코드가 컴파일되지 않습니다. 둘째, 컴파일 할 코드가 고정되어있을 때 (특히 Assert.TrueAssert.AreEqual으로 변경됨) 예상 한 결과가 나타납니다. 그리고 그것은 올바른 행동으로 좋은 것입니다. 하지만 당신은 Object.Equals을 오버라이드하지 않는 상속자에 의존 할 수 없기 때문에 비교를 기본 클래스로만 수행하려면 IEqualityComparer<BaseClass>을 구현해야합니다. 이 컴파일 않도록 당신은 아마 그것을 의도 한대로 여기

이 코드의 버전입니다 :

abstract class BaseClass { 
    public BaseClass(int x) { X = x; } 

    public int X { get; private set; } 

    public override bool Equals(object other) { 
     if (!(other is BaseClass)) { 
      return false; 
     } 

     BaseClass otherBaseClass = (BaseClass)other; 
     return (otherBaseClass.X == this.X); 
    } 

    public BaseClass ToBaseClass() { 
     return (BaseClass)this; 
    } 
} 

class ClassA : BaseClass { 
    public ClassA(int x, int y) : base (x) { 
     Y = y; 
    } 

    public int Y { get; private set; } 
} 

class ClassB : BaseClass { 
    public ClassB(int x, int z) : base (x) { 
     Z = z; 
    } 

    public int Z { get; private set; } 
} 

class Program { 
    static void Main(string[] args) { 
     var a = new ClassA(1, 2); 
     var b = new ClassB(1, 3); 
     Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass()); 
    } 
} 
1

나는 진지하게 내가 저자 아니라고하더라도 (KellermanSoftware.CompareNetObjects를 사용하는 것이 좋습니다 - 그것은 효과가 매우 유연하고 지금까지는 버그가 없습니다!).

나는 기본 클래스를 기반으로 2 개의 객체를 비교하는 데 동일한 작업을 수행했습니다.

다음
using System; 
using KellermanSoftware.CompareNetObjects; 

namespace Compare 
{ 

    /// <summary> 
    /// This allows us to compare objects based on a particular class (ie so we can compare on base classes) 
    /// </summary> 
    public class BaseClassComparer : KellermanSoftware.CompareNetObjects.TypeComparers.ClassComparer 
    { 


     private readonly Type _compareType; 
     internal BaseClassComparer(Type compareType, RootComparer rootComparer) : base(rootComparer) 
     { 

      _compareType = compareType; 
     } 

     public override void CompareType(CompareParms parms) 
     { 
      parms.Object1Type = _compareType; 
      parms.Object2Type = _compareType; 

      base.CompareType(parms); 
     } 

     public override bool IsTypeMatch(Type type1, Type type2) 
     { 
      if (((_compareType.IsAssignableFrom(type1)) && (_compareType.IsAssignableFrom(type2)))) { 
       return true; 
      } else { 
       return false; 
      } 
     } 
    } 
} 

은 켈러의 비교 자에이 클래스를 추가합니다 :

_compare = New CompareLogic 
_compare.Config.CustomComparers.Add(New BaseClassComparer(compareType, RootComparerFactory.GetRootComparer())) 

을 멀리 비교

1)은 비교에 사용할 형식을 취하는 "BaseClassComparer"을 만듭니다. 기본 유형 (compareType) 간의 차이점 만 검사 /보고됩니다.

관련 문제