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

发布于 2024-08-03 03:57:30 字数 2446 浏览 12 评论 0原文

我在使用 except() 方法时遇到问题。 它不返回差异,而是返回原始集合。

我尝试在 Account 类中实现 IEquatable 和 IEqualityComparer 。 我还尝试为 Account 创建一个单独的 IEqualityComparer 类。

当从 main 调用 except() 方法时,它似乎没有调用我自定义的 Equals() 方法,但是当我尝试 Count() 时,它确实调用了自定义的 GetHashCode() 方法!

我确信我在某个地方犯了一个小错误,我希望一双新的眼睛可以帮助我。

主要:

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

帐户:

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

I'm having troubles with the Except() method.
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.

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!

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

长亭外,古道边 2024-08-10 03:57:30

基本上,当您仅传入一个函数时,您的 LambdaComparer 类就会被破坏,因为如果您不提供其他任何内容,它就会使用“身份哈希代码”提供程序。哈希码由 Except 使用,这就是导致问题的原因。

这里有三个选项:

  1. 实现您自己的 ExceptBy 方法,然后最好将其贡献给 MoreLINQ 包含此类内容。

  2. 使用 IEqualityComparer 的不同实现。我有一个 ProjectionEqualityComparer 类,您可以在 MiscUtil 中使用 - 或者您可以使用另一个问题中发布的代码。< /p>

  3. 将 lambda 表达式传递到您的 LambdaComparer 代码中以用于哈希:

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

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. The hash code is used by Except, and that's what's causing the problem.

Three options here:

  1. Implement your own ExceptBy method and then preferably contribute it to MoreLINQ which contains that sort of thing.

  2. Use a different implementation of IEqualityComparer<T>. I have a ProjectionEqualityComparer class you can use in MiscUtil - or you can use the code as posted in another question.

  3. Pass a lambda expression into your LambdaComparer code to use for the hash:

    new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)),
                                x => x.IDPartner.GetHashCode());
    
╭ゆ眷念 2024-08-10 03:57:30

您还可以快速修复 LambdaComparer,使其在仅提供相等参数时正常工作,如下所示:

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

You could also quickly fix your LambdaComparer to work when only the equality parameters are supplied like this:

    public LambdaComparer(Func<T, T, bool> lambdaComparer) :
        this(lambdaComparer, o => 1)
    {
    }
茶底世界 2024-08-10 03:57:30

看这里,如何通过 linq.Except 及其他方式使用和实现 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; }

}

公共类 DepartmentComparer : 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.

Look here, how to use and implementing IEqualityComparer in way with linq.Except and beyond.

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 {

// 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.
青春如此纠结 2024-08-10 03:57:30

IMO,与此问题的其他解决方案相比,上面的答案是最简单的解决方案。我对其进行了调整,以便对 Object 类的 Equals() 和 GetHasCode() 使用相同的逻辑。好处是这个解决方案对于客户端 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();
    }    
}

IMO, this answer above is the simplest solution compared to other solutions for this problem. I tweaked it such that I use the same logic for the Object class's Equals() and GetHasCode(). The benefit is that this solution is completely transparent to the client linq expression.

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();
    }    
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文