如何在lambda表达式中为匿名变量编写IEqualityComparer?

时间:2017-02-25 11:51:13

标签: c# linq lambda

我想知道有一种方法可以在lambda表达式中为匿名变量实现IEqualityComparer,或者在任何情况下,我需要编写类来将匿名变量转换为sprecific类并创建一个我需要实现IEqualtyComparer的类?

我编写了创建笛卡尔(Decart' s)作品的代码: 我定义了Decart类。

public class Decart
{
    public int X;
    public int Y;
}

我为Decart类定义了IEqualtityComparer

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

我运行此代码:

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
}

我想运行下一个代码而不为匿名变量定义一个类,一个用于实现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

怎么做?

2 个答案:

答案 0 :(得分:6)

您可以声明IEqualityComparer<T>实现,该实现将委托作为接口GetHashCodeEquals方法的实现。然后你可以传入匿名方法作为实现。

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

你这样使用它:

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 }的含义:

需要CreateEqualityComparer(T ignore)的第一个参数,以允许编译器推断类型T,因为您不能拼写匿名类型的名称。三元运算符的true条件使编译器始终选择左分支null,但由于三元运算符的两个分支必须返回相同的类型,然后new { x = 0, y = 0 }使编译器隐式转换null给予匿名类型。

此外,规范中的相关说明:

  

7.6.10.6匿名对象创建表达式
  在同一个程序中,两个匿名对象初始值设定项以相同的顺序指定相同名称和编译时类型的属性序列,这将生成相同匿名类型的实例。

答案 1 :(得分:2)

目前还不清楚你在问什么。

我假设你想要像

那样确定这个类的维度
public class Decart
{
    public int[] dim;
}

然后你可以扩展比较器

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