IEqualityComparer for anonymous type

Firstly, I saw IEqualityComparer for the anonymous type , and the answers there do not answer my question, for the obvious reason that I need IEqualityComparer not and IComparer for use with the Linq Distinct() method. I also checked other answers, and this does not match the solution ...

Problem

I have code to manipulate and write records from a DataTable

 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(); 

but I need a separate method to be case insensitive. I am thrown using anonymous types.

Attempted Solution 1

If I had SomeClass that had specific objects, I could explicitly

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

I could do it

 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 :] 

Where

 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; } } 

However, using anonymous types in the above Linq query throws me away.

Attempted Solution 2

To try a different solution for this (and because I have the same problem elsewhere), I created the following general comparison class

 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); } } 

so that I can try to do

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

but this returns the return value as IEnumerable<dynamic> , which in turn affects my upcoming use of cflist , so the join fails in the next request.

  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 { ... } 

I don't want to get into ugly casting to and from IEnumerable<T> due to the heavy use of this code ...

Question

Is there a way to create my IEquailityComparer for my anonymous types?

Thank you for your time.

+6
source share
2 answers

Is there a way to create my IEquailityComparer for my anonymous types?

Sure. You just need to use type inference. For example, you might have something like:

 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. } } 

Then:

 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); 

Basically, the first parameter of the method is used only for type inference, so the compiler can determine which type to use for the parameters of the lambda expression.

You can create a comparison ahead of time by creating an anonymous type pattern:

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

but then at any time when you change the request, you need to also change the sample.

+11
source

This post can get what you want. Although for .NET 2.0 it also works for newer versions (see the bottom of this post for how to do this). Unlike the Jon Skeets solution, we will not use the factory method, for example create. I think this is just syntactic sugar.

+1
source

Source: https://habr.com/ru/post/975532/


All Articles