简体   繁体   English

IEnumerable.Except() 和自定义比较器

[英]IEnumerable.Except() and a custom comparer

I'm having troubles with the Except() method.我在使用 Except() 方法时遇到问题。 Instead of returning the difference, it returns the original set.它不返回差异,而是返回原始集。

I've tried implementing the IEquatable and IEqualityComparer in the Account class. I've also tried creating a separate IEqualityComparer class for Account.我尝试在帐户 class 中实施 IEquatable 和 IEqualityComparer。我还尝试为帐户创建一个单独的 IEqualityComparer class。

When the Except() method is called from main, it doesn't seem to call my custom Equals() method, but when I tried Count(), it did call the custom GetHashCode() method!当从 main 调用 Except() 方法时,它似乎没有调用我的自定义 Equals() 方法,但是当我尝试 Count() 时,它确实调用了自定义 GetHashCode() 方法!

I'm sure I made a trivial mistake somewhere and I hope a fresh pair of eyes can help me.我确定我在某个地方犯了一个微不足道的错误,我希望一双新鲜的眼睛能帮助我。

main:主要的:

IEnumerable<Account> everyPartnerID = 
    from partner in dataContext.Partners
    select new Account { IDPartner = partner.ID, Name = partner.Name };


IEnumerable<Account> hasAccountPartnerID = 
    from partner in dataContext.Partners
    from account in dataContext.Accounts
    where
        !partner.ID.Equals(Guid.Empty) &&
        account.IDPartner.Equals(partner.ID) &&
        account.Username.Equals("Special")
    select new Account { IDPartner = partner.ID, Name = partner.Name };

IEnumerable<Account> noAccountPartnerID = 
    everyPartnerID.Except(
        hasAccountPartnerID, 
        new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)));

Account:帐户:

    public class Account : IEquatable<Account>
    {
        public Guid IDPartner{ get; set; }
        public string Name{ get; set; }

/*        #region IEquatable<Account> Members

        public bool Equals(Account other)
        {
            return this.IDPartner.Equals(other.IDPartner);
        }

        #endregion*/
    }

LambdaComparer:拉姆达比较器:

   public class LambdaComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _lambdaComparer;
        private readonly Func<T, int> _lambdaHash;

        public LambdaComparer(Func<T, T, bool> lambdaComparer) :
            this(lambdaComparer, o => o.GetHashCode())
        {
        }

        public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
        {
            if (lambdaComparer == null)
                throw new ArgumentNullException("lambdaComparer");
            if (lambdaHash == null)
                throw new ArgumentNullException("lambdaHash");

            _lambdaComparer = lambdaComparer;
            _lambdaHash = lambdaHash;
        }

        public bool Equals(T x, T y)
        {
            return _lambdaComparer(x, y);
        }

        public int GetHashCode(T obj)
        {
            return _lambdaHash(obj);
        }
    }

Basically your LambdaComparer class is broken when you pass in just a single function, because it uses the "identity hash code" provider if you don't provide anything else.基本上,当您只传入一个函数时,您的LambdaComparer类就会被破坏,因为如果您不提供其他任何东西,它就会使用“身份哈希代码”提供程序。 The hash code is used by Except , and that's what's causing the problem.散列码由Except ,这就是导致问题的原因。

Three options here:这里的三个选项:

  1. Implement your own ExceptBy method and then preferably contribute it to MoreLINQ which contains that sort of thing.实现您自己的ExceptBy方法,然后最好将其贡献给包含此类内容的MoreLINQ

  2. Use a different implementation of IEqualityComparer<T> .使用IEqualityComparer<T>的不同实现。 I have a ProjectionEqualityComparer class you can use in MiscUtil - or you can use the code as posted in another question .我有一个ProjectionEqualityComparer类,您可以在MiscUtil 中使用 - 或者您可以使用在另一个问题中发布的代码。

  3. Pass a lambda expression into your LambdaComparer code to use for the hash:将 lambda 表达式传递到您的LambdaComparer代码中以用于哈希:

     new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)), x => x.IDPartner.GetHashCode());

You could also quickly fix your LambdaComparer to work when only the equality parameters are supplied like this:您还可以快速修复您的 LambdaComparer 以在仅提供等式参数时工作,如下所示:

    public LambdaComparer(Func<T, T, bool> lambdaComparer) :
        this(lambdaComparer, o => 1)
    {
    }

Look here, how to use and implementing IEqualityComparer in way with linq.Except and beyond.看这里,如何使用和实现 IEqualityComparer 与 linq.Except 及其他方式。

https://www.dreamincode.net/forums/topic/352582-linq-by-example-3-methods-using-iequalitycomparer/ https://www.dreamincode.net/forums/topic/352582-linq-by-example-3-methods-using-iequalitycomparer/

public class Department {
public string Code { get; set; }
public string Name { get; set; }

} }

public class DepartmentComparer : IEqualityComparer {公共类部门比较器:IEqualityComparer {

// equal if their Codes are equal
public bool Equals(Department x, Department y) {
    // reference the same objects?
    if (Object.ReferenceEquals(x, y)) return true;

    // is either null?
    if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
        return false;

    return x.Code == y.Code;
}

public int GetHashCode(Department dept) {
    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.

    // if null default to 0
    if (Object.ReferenceEquals(dept, null)) return 0;

    return dept.Code.GetHashCode();
}

} }

IEnumerable<Department> deptExcept = departments.Except(departments2, 
    new DepartmentComparer());

foreach (Department dept in deptExcept) {
    Console.WriteLine("{0} {1}", dept.Code, dept.Name);
}
// departments not in departments2: AC, Accounts.

IMO, this answer above is the simplest solution compared to other solutions for this problem. IMO,与此问题的其他解决方案相比,上面的答案是最简单的解决方案。 I tweaked it such that I use the same logic for the Object class's Equals() and GetHasCode().我对其进行了调整,以便对 Object 类的 Equals() 和 GetHasCode() 使用相同的逻辑。 The benefit is that this solution is completely transparent to the client linq expression.好处是这个解决方案对客户端 linq 表达式是完全透明的。

public class Ericsson4GCell
{
    public string CellName { get; set; }
    public string OtherDependantProperty { get; set; }

    public override bool Equals(Object y)
    {
        var rhsCell = y as Ericsson4GCell;
        // reference the same objects?
        if (Object.ReferenceEquals(this, rhsCell)) return true;

        // is either null?
        if (Object.ReferenceEquals(this, null) || Object.ReferenceEquals(rhsCell, null))
            return false;

        return this.CellName == rhsCell.CellName;
    }
    public override int GetHashCode()
    {
        // If Equals() returns true for a pair of objects 
        // then GetHashCode() must return the same value for these objects.

        // if null default to 0
        if (Object.ReferenceEquals(this, null)) return 0;

        return this.CellName.GetHashCode();
    }    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM