Reputation: 577
I want to know there is a way to implement IEqualityComparer for anonymous variables in a lambda expression or, in any case, I need to write class to transform anonymous variables to the sprecific class and create a class where I need to implement IEqualtyComparer ?
I write code which create Cartesian(Decart's) production: I defined class Decart.
public class Decart
{
public int X;
public int Y;
}
I defined IEqualtityComparer for Decart class
public class Ext : IEqualityComparer<Decart>
{
public bool Equals(Decart x, Decart y)
{
if ((x.X == y.X && x.Y == y.Y) ||
x.X == y.Y && x.Y == y.X)
return true;
return false;
}
public int GetHashCode(Decart obj)
{
return obj.X + obj.Y;
}
}
I run this code:
static void Main(string[] args)
{
Ext ext = new Ext();
IEnumerable<int> input = Enumerable.Range(1, 3);
var secondResult = input
.SelectMany(x => input.Select(y => new Decart{ X = x, Y = y }))
.Distinct(new Ext());
Console.WriteLine(new string('-', 50));
foreach (var x in secondResult)
{
Console.WriteLine(string.Format("{0} {1}", x.X, x.Y));
}
//output:
//1 1
//1 2
//1 3
//2 2
//2 3
//3 3
}
I want to run next code without defining a class for anonymous variables, a class for implementing IEqualityComparer.
var thirdResult = input
.SelectMany(x => input
.SelectMany(y => input
.Select(z => new { x, y, z })))
.Distinct( ???? );
//in this case output need to be like this:
//1 1 1
//1 2 1
//1 3 1
//2 1 2
//2 2 2
//2 3 2
//3 1 3
//3 2 3
//3 3 3
How to do it ?
Upvotes: 4
Views: 5176
Reputation: 22132
You can declare IEqualityComparer<T>
implementation which would take delegates as implementation for GetHashCode
and Equals
methods of interface. Then you can pass-in anonymous methods as implementation.
public static IEqualityComparer<T> CreateEqualityComparer<T>(T ignore, Func<T, int> getHashCode, Func<T, T, bool> equals) => new DelegatedEqualityComparer<T>(getHashCode, equals);
public class DelegatedEqualityComparer<T> : EqualityComparer<T> {
private Func<T, int> getHashCode;
private Func<T, T, bool> equals;
public DelegatedEqualityComparer(Func<T, int> getHashCode, Func<T, T, bool> equals) {
if(getHashCode==null) throw new ArgumentNullException(nameof(getHashCode));
if(equals==null) throw new ArgumentNullException(nameof(equals));
this.getHashCode=getHashCode;
this.equals=equals;
}
public override int GetHashCode(T x) => getHashCode(x);
public override bool Equals(T x, T y) => equals(x, y);
}
And you use it like this:
var equalityComparer = CreateEqualityComparer(true ? null : new { x = 0, y = 0 }, a => a.x+a.y, (a, b) => (a.x==b.x&&a.y==b.y)||(a.x==b.y&&a.y==b.x));
var result = input
.SelectMany(x => input
.Select(y => new { x, y }))
.Distinct(equalityComparer);
true ? null : new { x = 0, y = 0 }
:First argument to CreateEqualityComparer (T ignore
) is needed to allow compiler to infer type T
, as you can not spell name of anonymous type. true
condition of ternary operator make compiler to always select left branch null
, but as both branches of ternary operator must return the same type, then new { x = 0, y = 0 }
make compiler to implicitly cast null
to given anonymous type.
Also, relevant note from specification:
7.6.10.6 Anonymous object creation expressions
Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and compile-time types in the same order will produce instances of the same anonymous type.
Upvotes: 7
Reputation:
It's unclear what you're asking.
I assume you want to generilize the dimensions of this class like
public class Decart
{
public int[] dim;
}
then you can extend the comparer
public class Ext : IEqualityComparer<Decart>
{
public bool Equals(Decart x, Decart y)
{
for(int i=0; i< x.dim.Length;i++)
if (x.dim[i] != y.dim[i] )
return false;
return true;
}
public int GetHashCode(Decart obj)
{
return obj.dim.Sum();
}
}
Upvotes: 2