比较两个列表并返回不匹配的项目,结果为错误

时间:2015-06-12 02:30:44

标签: c# linq list

我尝试使用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列表中缺少哪个项目。

4 个答案:

答案 0 :(得分: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<Sent>以使其发挥作用。

IEqualityComparer<Sent>实现可能如下所示:

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的结果不应该在对象的整个生命周期中发生变化,否则您无法在字典或依赖于哈希代码的任何其他数据结构中使用该对象。

因此,为了使其有效,您需要更改Address&amp; Data是只读的。

以下是您的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;
    }
}

答案 1 :(得分: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 :(得分:1)

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

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

答案 3 :(得分:0)

如果您习惯使用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

如果你经常将结果映射到发送,你可以进一步缩短你的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 };
    }        
}