在List< [linq_custom_object]>()上实现.Distinct()的正确方法是什么?

时间:2014-03-13 14:30:05

标签: c# .net linq distinct

我的这个班级DNS_Log有四个属性。我已经创建了一个这些对象的列表,我试图过滤这些对象仅用于不同的事件。 (当填充列表时,有很多重复)

此处列出了正在填充的商家信息:

dnsLogs.Add( new DNS_Log { Destination = destination, 
                           Source_IP = sourceIp, 
                           Domain_Controller = domainController, 
                           DateTime = datetime });

这是我试图过滤掉不同的那些:

dnsLogs = dnsLogs.Distinct().ToList();

为什么这不起作用?我是否需要在不同的参数中使用一些linq表达式?我想将对象作为一个整体来比较它们的属性。有没有更简单的方法呢?

P.S。我已经开始制作一个似乎工作正常的自定义IEqualityComparer<DNS_Log>,但我不知道如何在这种情况下实现它。

3 个答案:

答案 0 :(得分:5)

您有多种选择:

  1. DNS_Log
  2. 类型上实施IEquatable<T>
  3. 在不实施IEquatable<T>
  4. 的情况下覆盖Equals和GetHashCode
  5. 实施单独的IEqualityComparer<T>并将其传递给Distinct
  6. 注意!在下面的所有代码中,相等性检查假定==运算符知道如何处理每种类型。对于DateTime成员来说肯定是这样(假设它的也是类型的DateTime),但我显然不能保证其他人会工作。如果Destination成员拥有尚未定义==运算符的类型,则可能会执行错误操作。由于您还没有为此比较器实施发布自己的代码,因此无法知道该怎么做。

    IEquatable<T>

    public class DNS_Log : IEquatable<DNS_Log>
    {
    
        public bool Equals(DNS_Log other)
        {
            if (other == null)
                return false;
    
            return (other.Destination == Destination
                    && other.Source_IP == Source_IP
                    && other.Domain_Controller == Domain_Controller
                    && other.DateTime == DateTime);
        }
    
        public override int GetHashCode()
        {
            int hash = 23;
            hash = hash * 59 + (Destination == null ? 0 : Destination.GetHashCode());
            hash = hash * 59 + (Source_IP == null ? 0 : Source_IP.GetHashCode());
            hash = hash * 59 + (Domain_Controller == null ? 0 : Domain_Controller.GetHashCode());
            hash = hash * 59 + DateTime.GetHashCode();
            return hash;
        }
    }
    

    覆盖不带接口的Equals和GetHashCode

    public class DNS_Log
    {
    
        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            var other = obj as DNS_Log;
            if (other == null) return false;
    
            ... rest the same as above
    

    单独IEqualityComparer<T>

    最后,您可以在致电Distinct时提供IEqualityComparer<T>

    dnsLogs = dnsLogs.Distinct(new DNS_LogEqualityComparer()).ToList();
    
    public class DNS_LogEqualityComparer : IEqualityComparer<DNS_Log>
    {
        public int GetHashCode(DNS_Log obj)
        {
            int hash = 23;
            hash = hash * 59 + (obj.Destination == null ? 0 : obj.Destination.GetHashCode());
            hash = hash * 59 + (obj.Source_IP == null ? 0 : obj.Source_IP.GetHashCode());
            hash = hash * 59 + (obj.Domain_Controller == null ? 0 : obj.Domain_Controller.GetHashCode());
            hash = hash * 59 + obj.DateTime.GetHashCode();
            return hash;
        }
    
        public bool Equals(DNS_Log x, DNS_Log y)
        {
            if (ReferenceEquals(x, y)) return true;
            if (x == null) return false;
    
            return (x.Destination == y.Destination
                && x.Source_IP == y.Source_IP
                && .Domain_Controller == y.Domain_Controller
                && x.DateTime == y.DateTime);
        }
    }
    

答案 1 :(得分:2)

你基本上有三个选择:

  • 在致电IEqualityComparer<DNS_Log>时提供您的自定义Distinct(类似dnsLogs.Distinct(myEqualityComparerInstance).ToList();
  • DNS_Log实施IEquatable<DNS_Log>
  • Equals
  • 上覆盖GetHashCodeDNS_Log

答案 2 :(得分:1)

  

为什么这不起作用?

它不起作用,因为Distinct使用GetHashCode + Equals来比较对象。由于您没有从对象覆盖这些方法,因此您将获得仅比较引用的默认实现。

所以你有几个选择:

  1. 您可以为Distinct
  2. 的重载实施自定义IEqualityComparer<T>
  3. 或覆盖您班级中的Equals + GethashCode
  4. 不需要创建新类或修改现有类的另一种(效率较低)方法是使用匿名类型的内置GetHashCode + Equals和{ {1}}:

    Enumerable.GroupBy
  5. 这是第二种方法的一个例子:

    IEnumerable<DNS_Log> distinctLogs = 
        from dns in dnsLogs
        group dns by  new { dns.Destination, dns.Source_IP,dns.Domain_Controller, dns.DateTime } into g 
        select g.First(); // change logic if you don't want an arbitrary object(first)
    

    public class DNS_Log { public string Destination{ get; set; } public int Source_IP { get; set; } public string Domain_Controller { get; set; } public DateTime DateTime { get; set; } public override bool Equals(object obj) { DNS_Log c2 = obj as DNS_Log; if (obj == null) return false; return Destination == c2.Destination && Source_IP == c2.Source_IP && Domain_Controller == c2.Domain_Controller && DateTime == c2.DateTime; } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + Destination.GetHashCode(); hash = hash * 23 + Source_IP; hash = hash * 23 + Domain_Controller.GetHashCode(); hash = hash * 23 + DateTime.GetHashCode(); return hash; } } } - 类({方法)的EqualsGethashCode类似。