【问题标题】:Comparing two lists and return not matching items results with error比较两个列表并返回不匹配的项目结果错误
【发布时间】:2015-06-12 02:30:44
【问题描述】:

我尝试使用Except 方法比较两个列表。但是当我这样做时,我收到一条错误消息:

无法从“Systems.Collections.Generic.List”转换为“System.Linq.IQueryable”

'System.Collections.Generic.List 不包含 'Except' 的定义和最佳扩展方法重载 'System.Linq.Queryable.Except(System.Linq.IQueryable, System.Collections.GEneric.IEnumerable )' 有一些无效的参数

我在尝试Intersect 时也遇到过这种情况。我正在尝试比较已发送列表和结果列表(代码和列表如下所示)并返回没有任何匹配项的项目。所以当我在谷歌上搜索如何做到这一点时,我遇到了 except 方法以及 Intersect。

public class Sent
{
    public string Address;
    public string Data;
}

public class Result
{
    public string AddressOK;
    public string DataOK;
}



var sent = new List<Sent>();
sent.Add(new Sent() { Address = linaddr1, Data = lindat1 });
var res = new List<Result>();
res.Add( new Result() { AddressOK = linaddr2, DataOK = lindat2 } );
//linaddr1 and 2, lindat1 and 2 contains the address and data shown in the list below
//taken from another part of the entire program

列表如下所示:

sent                       res
Address    Data            Address        Data
04004C     55AA55          04004C         55AA55
040004     0720            040004         0720
040037     30           
04004A     FFFF            04004A         FFFF

我只尝试使用此代码: var diff = sent.Except(res).ToList() 但正如我所提到的,它会导致上述错误。

编辑:我编辑了列表。对此感到抱歉。这只是 res 列表缺少原始列表中的一个或两个或多个项目然后比较两个列表以查看 res 列表中缺少哪个项目的问题。

【问题讨论】:

  • 你导入Linq了吗?使用 System.Linq;
  • 为什么会有两个完全一样的独立类?只是为了可读性目的?如果sentres 都是List&lt;Sent&gt;,它是否有效?
  • @Loocid - 不,如果两者都是List&lt;Sent&gt;,它就不起作用。

标签: c# linq list


【解决方案1】:

任意使用:

using System;

using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {

        var sent = new List<Sent>()
        {
            new Sent { Address = "04004C", Data = "55AA55" },
            new Sent { Address = "040004", Data = "0720" },             
            new Sent { Address = "040037", Data = "31" },               
            new Sent { Address = "04004A", Data = "FFFF" }
        };


        var res = new List<Result> () {
            new Result { AddressOK = "04004C", DataOK = "55AA55" },
            new Result { AddressOK = "040004", DataOK = "0721" },               
            new Result { AddressOK = "040038 ", DataOK = "31" },                
            new Result { AddressOK = "04004A", DataOK = "FFFF" }

        };

        var diff =
            sent.Where (s => !res.Any (r => s.Address == r.AddressOK && s.Data == r.DataOK ));


        foreach (var item in diff) 
        {
            Console.WriteLine("{0} {1}", item.Address, item.Data);
        }

    }
}


public class Sent
{
    public string Address;
    public string Data;
}


public class Result
{
    public string AddressOK;
    public string DataOK;
}

输出:

040004 0720
040037 31

直播代码:https://dotnetfiddle.net/ZVuiPd

【讨论】:

    【解决方案2】:

    SentResult 类型是不同的类型,但 sent.Except(res) 期望它们是相同的。这是你的第一个错误。

    以下是一个简单(但不正确)的修复:

    var diff =
        sent
            .Except(res.Select(x => new Sent() { Address = x.AddressOK, Data = x.DataOK }))
            .ToList();
    

    即使编译并运行,它也不会删除重复项,因为您的 Sent 不会覆盖 GetHashCodeEquals,因此它只比较引用而不是实际属性。

    您可以实现 GetHashCodeEquals,或者创建一个 IEqualityComparer&lt;Sent&gt; 以使其工作。

    IEqualityComparer&lt;Sent&gt; 实现可能如下所示:

    public class SentEqualityComparer : IEqualityComparer<Sent>
    {
        public int GetHashCode(Sent sent)
        {
            return sent.Address.GetHashCode() ^ sent.Data.GetHashCode();
        }
    
        public bool Equals(Sent left, Sent right)
        {
            return (left.Address == right.Address) && (left.Data == right.Data);
        }
    }
    

    你会这样使用它:

    var diff =
        sent
            .Except(
                res.Select(x => new Sent() { Address = x.AddressOK, Data = x.DataOK }),
                new SentEqualityComparer())
            .ToList();
    

    这如您所愿。

    另一个选项,覆盖GetHashCodeEquals,有一个额外的障碍。 GetHashCode 的结果不应在对象的整个生命周期内发生变化,否则您无法在字典或任何其他依赖哈希码的数据结构中使用该对象。

    因此,要使其正常工作,您需要将 AddressData 更改为只读。

    这是您的 Sent 类的实现,它可以正常工作:

    public sealed class Sent : IEquatable<Sent>
    {
        private readonly string _Address; 
        private readonly string _Data;
    
        public string Address { get { return _Address; } } 
        public string Data { get { return _Data; } }
    
        public Sent(string Address, string Data)
        {
            _Address = Address; 
            _Data = Data;    
        }
    
        public override bool Equals(object obj)
        {
            if (obj is Sent)
                    return Equals((Sent)obj);
            return false;
        }
    
        public bool Equals(Sent obj)
        {
            if (obj == null) return false;
            if (!EqualityComparer<string>.Default.Equals(_Address, obj._Address)) return false; 
            if (!EqualityComparer<string>.Default.Equals(_Data, obj._Data)) return false;    
            return true;
        }
    
        public override int GetHashCode()
        {
            int hash = 0;
            hash ^= EqualityComparer<string>.Default.GetHashCode(_Address); 
            hash ^= EqualityComparer<string>.Default.GetHashCode(_Data);
            return hash;
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果您愿意使用AOP 组件来自动化实现 IEquatable 的手动代码,另一种方法是使用Equals.Fody

      using System;    
      using System.Collections.Generic;
      using System.Linq;
      
      public class Program
      {
          public static void Main()
          {
              var a = new Sent { Address = "04004C", Data = "55AA55" };
              var b = new Sent { Address = "04004C", Data = "55AA55" };
      
              Console.WriteLine(a.Equals(b)); // True with use of an AOP, False with no AOP           
      
              var sent = new List<Sent>() {
                  new Sent { Address = "04004C", Data = "55AA55" },
                  new Sent { Address = "040004", Data = "0720" },             
                  new Sent { Address = "040037", Data = "31" },               
                  new Sent { Address = "04004A", Data = "FFFF" }
              };
      
              var res = new List<Result>() {
                  new Result { AddressOK = "04004C", DataOK = "55AA55" },
                  new Result { AddressOK = "040004", DataOK = "0721" },               
                  new Result { AddressOK = "040038 ", DataOK = "31" },                
                  new Result { AddressOK = "04004A", DataOK = "FFFF" }    
              };
      
      
              var diff =
                  sent.Except(
                      res.Select(r => new Sent { Address = r.AddressOK, Data = r.DataOK })
                  );
      
              foreach (var item in diff)
                  Console.WriteLine("{0} {1}", item.Address, item.Data);           
      
          }
      }
      
      
      [Equals]
      public class Sent
      {
          public string Address;
          public string Data;
      
          [CustomEqualsInternal]
          bool CustomLogic(Sent other)
          {
              return other.Address == this.Address && other.Data == this.Data;
          }
      }
      
      public class Result
      {
          public string AddressOK;
          public string DataOK;
      }
      

      输出:

      True
      040004 0720
      040037 31
      

      如果您经常将 Result 映射到 Sent,您可以进一步将 Linq 查询代码缩短为..

      var diff = sent.Except(res.Select(r => (Sent)r));
      

      ..通过自动将 Result 映射到 Sent,使用隐式运算符:

      [Equals]
      public class Sent
      {
          public string Address;
          public string Data;
      
          [CustomEqualsInternal]
          bool CustomLogic(Sent other)
          {
              return other.Address == this.Address && other.Data == this.Data;
          }
      
      
          public static implicit operator Sent(Result r)
          {
              return new Sent { Address = r.AddressOK, Data = r.DataOK };
          }        
      }
      

      【讨论】:

        【解决方案4】:

        @Kurisuchin 假设您有 2 个列表,并且在这两个列表中都有 ID 属性,您希望根据该属性比较两个列表并希望将不匹配的项目存储在第三个列表中。 在这种情况下,Linq Query 可以提供帮助。

        var 结果 = List2.Where(p => !List1.Any(p2 => p2.ID == p.ID)).ToList();

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-19
          • 1970-01-01
          • 2016-12-23
          • 2010-11-26
          • 1970-01-01
          • 2020-07-06
          • 1970-01-01
          相关资源
          最近更新 更多