【问题标题】:IEnumerable.Except() and a custom comparerIEnumerable.Except() 和自定义比较器
【发布时间】:2009-08-19 12:08:54
【问题描述】:

我在使用 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);
        }
    }

【问题讨论】:

    标签: c# linq linq-to-sql iequatable iequalitycomparer


    【解决方案1】:

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

    这里有三个选项:

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

    2. 使用 IEqualityComparer&lt;T&gt; 的不同实现。我有一个 ProjectionEqualityComparer 类,您可以在 MiscUtil 中使用 - 或者您可以使用代码 as posted in another question

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        看这里,如何使用和实现 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; }
        

        }

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

        【讨论】:

          【解决方案4】:

          IMO,this answer above 是与此问题的其他解决方案相比最简单的解决方案。我对其进行了调整,以便对 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();
              }    
          }
          

          【讨论】:

            猜你喜欢
            • 2017-11-02
            • 2016-10-20
            • 2018-12-22
            • 2015-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多