簡體   English   中英

IEnumerable.Except() 和自定義比較器

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

我在使用 Except() 方法時遇到問題。 它不返回差異,而是返回原始集。

我嘗試在帳戶 class 中實施 IEquatable 和 IEqualityComparer。我還嘗試為帳戶創建一個單獨的 IEqualityComparer class。

當從 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*/
    }

拉姆達比較器:

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

基本上,當您只傳入一個函數時,您的LambdaComparer類就會被破壞,因為如果您不提供其他任何東西,它就會使用“身份哈希代碼”提供程序。 散列碼由Except ,這就是導致問題的原因。

這里的三個選項:

  1. 實現您自己的ExceptBy方法,然后最好將其貢獻給包含此類內容的MoreLINQ

  2. 使用IEqualityComparer<T>的不同實現。 我有一個ProjectionEqualityComparer類,您可以在MiscUtil 中使用 - 或者您可以使用在另一個問題中發布的代碼。

  3. 將 lambda 表達式傳遞到您的LambdaComparer代碼中以用於哈希:

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

您還可以快速修復您的 LambdaComparer 以在僅提供等式參數時工作,如下所示:

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

看這里,如何使用和實現 IEqualityComparer 與 linq.Except 及其他方式。

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

}

公共類部門比較器: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,與此問題的其他解決方案相比,上面的答案是最簡單的解決方案。 我對其進行了調整,以便對 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();
    }    
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM