2014-09-19 12 views
6

먼저 IEqualityComparer for anonymous type을보고 거기에 대한 답변이 IEqualityComparer이 아니고 Linq의 Distinct() 메서드와 함께 사용하려면 IComparer이 필요하다는 명백한 이유 때문에 내 질문에 대한 대답이 없습니다. 내가 조작하고 DataTableAnnoymous Type에 대한 IEqualityComparer

var glext = m_dtGLExt.AsEnumerable(); 
var cflist = 
    (from c in glext 
    orderby c.Field<string>(m_strpcCCType), 
      c.Field<string>(m_strpcCC), 
      c.Field<string>(m_strpcCCDesc), 
      c.Field<string>(m_strpcCostItem) 
    select new 
    { 
     CCType = c.Field<string>(m_strpcCCType), 
     CC = c.Field<string>(m_strpcCC), 
     CCDesc = c.Field<string>(m_strpcCCDesc), 
     CostItem = c.Field<string>(m_strpcCostItem) 
    }).Distinct(); 

에서 레코드를 끌어 몇 가지 코드를 가지고 ...

문제점

를 너무 다른 답변을 확인하고 이러한 솔루션에 미달하지만, 대소 문자를 구별하는 고유 한 방법이 필요합니다. 나를 던지는 것은 익명의 타입을 사용하는 것입니다. 나는 구체적인 목적을 가지고 SomeClass이 있다면 나는 분명히

public class SumObject 
{ 
    public string CCType { get; set; } 
    public string CC { get; set; } 
    public string CCDesc { get; set; } 
    public string CostItem { get; set; } 
} 

을 할 수있는 해결 방법 1

시도

나는이

List<SumObject> lso = new List<SumObject>() 
{ 
    new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Rooney", CostItem = "I477" }, 
    new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Zidane", CostItem = "I677" }, 
    new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Falcao", CostItem = "I470" }, 
}; 
var e = lso.Distinct(new SumObjectComparer()); // Great :] 

class SumObjectComparer : IEqualityComparer<SumObject> 
{ 
    public bool Equals(SumObject x, SumObject y) 
    { 
     if (Object.ReferenceEquals(x, y)) 
      return true; 
     if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) 
      return false; 
     return x.CCType.CompareNoCase(y.CCType) == 0 && 
       x.CC.CompareNoCase(y.CC) == 0 && 
       x.CCDesc.CompareNoCase(y.CCDesc) == 0 && 
       x.CostItem.CompareNoCase(y.CostItem) == 0; 
    } 

    public int GetHashCode(SumObject o) 
    { 
     if (Object.ReferenceEquals(o, null)) 
      return 0; 
     int hashCCType = String.IsNullOrEmpty(o.CCType) ? 
      0 : o.CCType.ToLower().GetHashCode(); 
     int hashCC = String.IsNullOrEmpty(o.CC) ? 
      0 : o.CC.ToLower().GetHashCode(); 
     int hashCCDesc = String.IsNullOrEmpty(o.CCDesc) ? 
      0 : o.CCDesc.ToLower().GetHashCode(); 
     int hashCostItem = String.IsNullOrEmpty(o.CostItem) ? 
      0 : o.CostItem.ToLower().GetHashCode(); 
     return hashCCType^hashCC^hashCCDesc^hashCostItem; 
    } 
} 
0,123 할 분명히 수

그러나 위의 Linq 쿼리에서 익명 형식을 사용하면 문제가 발생합니다. 이 또 다른 솔루션을 시도하기 위해

해결이 시도

(나는 다른 곳에서 같은 문제를 가지고 있기 때문에) 내가

public class GenericEqualityComparer<T> : IEqualityComparer<T> 
{ 
    Func<T, T, bool> compareFunction; 
    Func<T, int> hashFunction; 

    public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction) 
    { 
     this.compareFunction = compareFunction; 
     this.hashFunction = hashFunction; 
    } 

    public bool Equals(T x, T y) { return compareFunction(x, y); } 
    public int GetHashCode(T obj) { return hashFunction(obj); } 
} 

내가 할 시도 할 수있는 다음과 같은 일반적인 비교 자 클래스를 생성

var comparer = new GenericEqualityComparer<dynamic>(
    (x, y) => { /* My equality stuff */ }, 
    o => { /* My hash stuff */ }); 

그러나이 값은 IEnumerable<dynamic>으로 반환됩니다. 다음번의 쿼리에서 이 실패하지 않도록 cflist을 사용할 예정입니다. 질문

var cf = 
    (from o in cflist 
    join od in glext 
    on new { o.CCType, o.CC, o.CCDesc, o.CostItem } equals new 
    { 
     CCType = od.Field<string>(m_strpcCCType), 
     CC = od.Field<string>(m_strpcCC), 
     CCDesc = od.Field<string>(m_strpcCCDesc), 
     CostItem = od.Field<string>(m_strpcCostItem) 
    } 
    into c 
    select new { ... } 
난에이 코드를 많이 사용으로 인해 IEnumerable<T> s의 추한 캐스팅에 들어갈 싶지 않아

...

는 방법은 내가 할 수 있는가 내 익명 유형에 대해 IEquailityComparer을 만드시겠습니까?

감사합니다.

+0

익명'select new' 개체를'select new SumObject' 개체로 바꾼 경우 첫 번째 해결책이 효과가있었습니다. 익명 객체를 사용하여 큰 문제는 무엇입니까? 이미 이름이 지정된 클래스를 만들었습니까? – dasblinkenlight

+0

나는 그럴 수없고 그럴 수 없다. 내가 할 수 있으면 쉬울 것입니다. 또 다른 몇 가지 장소가 어디에 이러한 일반적인 작업 linq 선택 쿼리 및 익명 형식으로 작업을 좀 더 일반적인 솔루션을 싶습니다 ... – MoonKnight

+0

그것은 느려질 것입니다,하지만 당신은 IEqualityComparer 작성하는 반사를 사용해야 할 것 같아요 모든 필드와 속성을 비교하십시오. –

답변

11

내 익명 형식에 대한 IEquailityComparer를 만들 수있는 방법이 있습니까?

확실히. 타입 유추 만하면됩니다.그런 다음

public static class InferredEqualityComparer 
{ 
    public static IEqualityComparer<T> Create<T>(
     IEnumerable<T> example, 
     Func<T, T, bool> equalityCheck, 
     Func<T, int> hashCodeProvider) 
    { 
     return new EqualityComparerImpl<T>(equalityCheck, hashCodeProvider); 
    } 

    private sealed class EqualityComparerImpl<T> : IEqualityComparer<T> 
    { 
     // Implement in the obvious way, remembering the delegates and 
     // calling them appropriately. 
    } 
} 

: 예를 들어, 당신이 뭔가를 할 수

var glext = m_dtGLExt.AsEnumerable(); 
var query = from c in glext 
      orderby ... 
      select new { ... }; 
var comparer = InferredEqualityComparer.Create(query, 
    (x, y) => { ... }, 
    o => { ... } 
); 
var distinct = query.Distinct(comparer); 

는 기본적으로 메소드의 첫 번째 매개 변수는 컴파일러가 운동 할 수 있도록 단지는, 타입 추론을 위해 사용되는 유형 람다 표현 매개 변수에 사용합니다.

당신 익명 타입의 샘플을 만들어 미리의 비교자를 만들 수 있습니다

var sample = new[] { new { ... } }; 
var comparer = InferredExqualityComparer.Create(sample, ...); 
var distinct = (... query here ...).Distinct(comparer); 

하지만

다음 당신은 당신이 너무 샘플을 변경할 수있어 쿼리를 변경할 수있는 시간입니다.

+1

Dammit Skeet 나는 이런 식으로 뭔가를 게시하려고했습니다. –

+0

안녕하세요, Jon, 답장을 보내 주셔서 감사합니다. 나는 당신이 참조한'lso' 객체를 가지고 있지 않습니다. 단지 이것을 Distinct()를 사용하여 구체적인 유형으로 사용할 수있는 예제로 사용했습니다. 질문의 첫 번째 코드 단편에서 LINQ 쿼리와 함께 위의 솔루션을 어떻게 사용할 수 있는지 보지 못합니다 ... – MoonKnight

+0

@Killercam : Edited. 기본적으로 "별개의"버전을 먼저 구한 다음 비교자를 만든 다음 "Distinct"를 호출해야합니다. –

1

This post 원하는 것을 얻을 수 있습니다. .NET 2.0의 경우에도 최신 버전에서 작동합니다 (이 게시물 하단의 방법 참조). Jon Skeets 솔루션과 달리 create와 같은 factory-method는 사용하지 않을 것입니다. 그러나 이것은 내가 생각하는 통사론적인 설탕 일뿐입니다.

+0

답변을 주셔서 감사합니다 :]. – MoonKnight