尝试通过Union将两个Lists<>合并,但仍存在重复项。

3

我有两个列表:

List<L1>, List<L2>

L1 = { detailId = 5, fileName = "string 1" }{ detailId = 5, fileName = "string 2" }
L2 = { detailId = 5, fileName = "string 2" }{ detailId = 5, fileName = "string 3" }

我希望能将它们合并在一起,且不重复出现:
List<L3>

L1 = { detailId = 5, fileName = "string 1" }{ detailId = 5, fileName = "string 2" }{ detailId = 5, fileName = "string 3" }

我尝试过:
L1.Union(L2).ToList();
L1.Concat(L2).Distinct().ToList();

但是两者都会返回重复的值(1,2,2,3)。

不确定我错过了什么。

编辑 这是该方法。它从一个分隔字符串中获取一个列表,并尝试将它们合并。

private List<Files> CombineList(int detailId, string fileNames)
{
    List<Files> f1 = new List<Files>();
    List<Files> f2 = new List<Files>();
    f1 = GetFiles(detailId, false);
    if (f1[0].fileNames != "")
    {
        string[] names = fileNames.Split('|');
        for (int i = 0; i < names.Length; i++)
        {
            Files x = new Files();
            x.detailId = detailId;
            x.fileNames = names[i];
            f2.Add(x);
        }
        List<Files> f3 = f1.Union(f2).ToList();
    }
    return f3;

}

1
对象是如何进行比较的?Union使用默认的相等比较器。 - vlad
我会在我正在使用的方法中进行编辑。 - todd.pund
那是因为你没有在 L1L2 上实现 IEquatable<T> - Steven
5个回答

6

来自MSDN,对于Union:

默认的相等比较器Default用于比较实现了IEqualityComparer(Of T)泛型接口的类型的值。要比较自定义数据类型,您需要实现此接口并为该类型提供自己的GetHashCode和Equals方法。

链接

由于您使用自定义类型,因此需要覆盖GetHashCodeEquals或提供一个实现IEqualityComparer接口(最好是IEquatable)的对象,并将其作为第二个参数提供给Union

这里有一个实现这样的类的简单示例。


他不必实现 IEqualityComparer<T>,只需重写 GetHashCode() 和 Equals(object) 即可。同时实现 IEquatable<T> 更好。 - Kris Vandermotten
这看起来是我需要的东西。我会仔细阅读并尝试实现它。 - todd.pund
你需要使用自定义类型,因此需要重写 GetHashCodeEquals 方法,并最好实现 IEquatable<T> 接口;或者提供一个实现了 IEqualityComparer<T> 接口的对象,并将其作为 Union 的第二个参数。 - Kris Vandermotten

3

我不喜欢覆盖Files类的equals对象和getHashCode方法,因为这样会干扰对象。让另一个对象来执行此操作,并只需传递它。这样,如果以后出现问题,只需更换并传递另一个IEqualityComparer

以下是您可以测试的示例

public void MainMethod()
{
    List<Files> f1 = new List<Files>() { new Files() { detailId = 5, fileName = "string 1" }, new Files() { detailId = 5, fileName = "string 2" } };
    List<Files> f2 = new List<Files>() { new Files() { detailId = 5, fileName = "string 2" }, new Files() { detailId = 5, fileName = "string 3" } };

    var f3 = f1.Union(f2, new FilesComparer()).ToList();
    foreach (var f in f3)
    {
    Console.WriteLine(f.detailId + " " + f.fileName);
    }

}

public class Files
{
    public int detailId;
    public string fileName;
}

public class FilesComparer : IEqualityComparer<Files>
{
    public bool Equals(Files x, Files y)
    {
        return x.fileName == y.fileName;
    }

    public int GetHashCode(Files obj)
    {
        return obj.fileName.GetHashCode();
    }
}

这和我使用Solarbear的建议想出来的解决方案非常相似。 - todd.pund

1
如果您的元素没有实现某种比较接口(Object.Equals,IEquatable,IComparable等),则它们之间的任何相等性测试都将涉及ReferenceEquals,在其中两个不同的对象是两个不同的对象,即使它们的所有成员包含相同的值。

2
IComparable 不会有所帮助。同时,需要重写 GetHashCode() 方法。 - Kris Vandermotten

1
如果你要合并一个对象列表,你需要为相等比较定义一些标准。下面的示例演示了这一点:
class MyModelTheUniqueIDComparer : IEqualityComparer<MyModel>
{
    public bool Equals(MyModel x, MyModel y)
    {
        return x.SomeValue == y.SomeValue && x.OtherValue == y.OtherValue;
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.

    public int GetHashCode(MyModel myModel)
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 31 + myModel.SomeValue.GetHashCode();
            hash = hash * 31 + myModel.OtherValue.GetHashCode();
            return hash;
        }
    }
}

然后您可以调用以获取结果:
var result = q1.Union(q2, new MyModelTheUniqueIDComparer());

他并不希望他们仅仅比较ID,而是希望基于两个属性来定义相等性,否则在他的例子中只会得到一个结果。 - Servy
现在你的equals和gethashcode方法没有使用相同的相等定义,这是非常糟糕的。 - Servy
@Servy:哈哈,刚看到了。给我一点时间 ;) - James Johnson

0

来自MSDN Enumerable.Union方法

如果您想比较自定义数据类型的对象序列,则必须在类中实现IEqualityComparer<T>泛型接口。

一个针对您的Files类的示例实现,以便在合并两个自定义类型的集合时Union能够正确工作:

public class Files : IEquatable<Files>
{
    public string fileName { get; set; }
    public int detailId { get; set; }

    public bool Equals(Files other)
    {

        //Check whether the compared object is null. 
        if (Object.ReferenceEquals(other, null)) return false;

        //Check whether the compared object references the same data. 
        if (Object.ReferenceEquals(this, other)) return true;

        //Check whether the products' properties are equal. 
        return detailId.Equals(other.detailId) && fileName.Equals(other.fileName);
    }

    // If Equals() returns true for a pair of objects  
    // then GetHashCode() must return the same value for these objects. 

    public override int GetHashCode()
    {

        //Get hash code for the fileName field if it is not null. 
        int hashFileName = fileName == null ? 0 : fileName.GetHashCode();

        //Get hash code for the detailId field. 
        int hashDetailId = detailId.GetHashCode();

        //Calculate the hash code for the Files object. 
        return hashFileName ^ hashDetailId;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接