C# Distinct 不能使用 LINQ to Objects

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1365748/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 15:42:16  来源:igfitidea点击:

Distinct not working with LINQ to Objects

c#.netlinqiequatableiequalitycomparer

提问by Tanmoy

class Program
{
    static void Main(string[] args)
    {
        List<Book> books = new List<Book> 
        {
            new Book
            {
                Name="C# in Depth",
                Authors = new List<Author>
                {
                    new Author 
                    {
                        FirstName = "Jon", LastName="Skeet"
                    },
                     new Author 
                    {
                        FirstName = "Jon", LastName="Skeet"
                    },                       
                }
            },
            new Book
            {
                Name="LINQ in Action",
                Authors = new List<Author>
                {
                    new Author 
                    {
                        FirstName = "Fabrice", LastName="Marguerie"
                    },
                     new Author 
                    {
                        FirstName = "Steve", LastName="Eichert"
                    },
                     new Author 
                    {
                        FirstName = "Jim", LastName="Wooley"
                    },
                }
            },
        };


        var temp = books.SelectMany(book => book.Authors).Distinct();
        foreach (var author in temp)
        {
            Console.WriteLine(author.FirstName + " " + author.LastName);
        }

        Console.Read();
    }

}
public class Book
{
    public string Name { get; set; }
    public List<Author> Authors { get; set; }
}
public class Author
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public override bool Equals(object obj)
    {
        return true;
        //if (obj.GetType() != typeof(Author)) return false;
        //else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
    }

}

This is based on an example in "LINQ in Action". Listing 4.16.

这是基于“LINQ in Action”中的一个例子。清单 4.16。

This prints Jon Skeet twice. Why? I have even tried overriding Equals method in Author class. Still Distinct does not seem to work. What am I missing?

这会打印 Jon Skeet 两次。为什么?我什至尝试在 Author 类中覆盖 Equals 方法。仍然 Distinct 似乎不起作用。我错过了什么?

Edit: I have added == and != operator overload too. Still no help.

编辑:我也添加了 == 和 != 运算符重载。还是没有帮助。

 public static bool operator ==(Author a, Author b)
    {
        return true;
    }
    public static bool operator !=(Author a, Author b)
    {
        return false;
    }

采纳答案by skalb

LINQ Distinct is not that smart when it comes to custom objects.

当涉及到自定义对象时,LINQ Distinct 并不是那么聪明。

All it does is look at your list and see that it has two different objects (it doesn't care that they have the same values for the member fields).

它所做的只是查看您的列表,看看它有两个不同的对象(它不关心它们的成员字段值是否相同)。

One workaround is to implement the IEquatable interface as shown here.

一个解决办法是实现IEquatable接口如图所示这里

If you modify your Author class like so it should work.

如果你像这样修改你的 Author 类,它应该可以工作。

public class Author : IEquatable<Author>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public bool Equals(Author other)
    {
        if (FirstName == other.FirstName && LastName == other.LastName)
            return true;

        return false;
    }

    public override int GetHashCode()
    {
        int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode();
        int hashLastName = LastName == null ? 0 : LastName.GetHashCode();

        return hashFirstName ^ hashLastName;
    }
}

Try it as DotNetFiddle

尝试作为 DotNetFiddle

回答by Rex M

The Distinct()method checks reference equality for reference types. This means it is looking for literally the same object duplicated, not different objects which contain the same values.

Distinct()方法检查引用类型的引用相等性。这意味着它实际上是在寻找重复的相同对象,而不是包含相同值的不同对象。

There is an overloadwhich takes an IEqualityComparer, so you can specify different logic for determining whether a given object equals another.

有一个采用IEqualityComparer重载,因此您可以指定不同的逻辑来确定给定对象是否等于另一个对象。

If you want Author to normally behave like a normal object (i.e. only reference equality), but for the purposes of Distinct check equality by name values, use an IEqualityComparer. If you always want Author objects to be compared based on the name values, then override GetHashCode and Equals, or implement IEquatable.

如果您希望 Author 通常表现得像一个普通对象(即仅引用相等性),但为了按名称值进行 Distinct 检查相等性,请使用IEqualityComparer。如果您总是希望根据名称值比较 Author 对象,则覆盖 GetHashCode 和 Equals,或实现 IEquatable

The two members on the IEqualityComparerinterface are Equalsand GetHashCode. Your logic for determining whether two Authorobjects are equal appears to be if the First and Last name strings are the same.

IEqualityComparer界面上的两个成员是EqualsGetHashCode。您确定两个Author对象是否相等的逻辑似乎是 First 和 Last name 字符串是否相同。

public class AuthorEquals : IEqualityComparer<Author>
{
    public bool Equals(Author left, Author right)
    {
        if((object)left == null && (object)right == null)
        {
            return true;
        }
        if((object)left == null || (object)right == null)
        {
            return false;
        }
        return left.FirstName == right.FirstName && left.LastName == right.LastName;
    }

    public int GetHashCode(Author author)
    {
        return (author.FirstName + author.LastName).GetHashCode();
    }
}

回答by Eric King

You've overriden Equals(), but make sure you also override GetHashCode()

您已经覆盖了 Equals(),但请确保您还覆盖了 GetHashCode()

回答by AndyM

Distinct()performs the default equality comparison on objects in the enumerable. If you have not overridden Equals()and GetHashCode(), then it uses the default implementation on object, which compares references.

Distinct()对枚举中的对象执行默认的相等比较。如果您没有覆盖Equals()and GetHashCode(),则它使用 on 的默认实现object,它比较引用。

The simple solution is to add a correctimplementation of Equals()and GetHashCode()to all classes which participate in the object graph you are comparing (ie Book and Author).

简单的解决办法是增加一个正确的实施Equals()GetHashCode()向参与您比较(即图书和作者)对象图的所有类。

The IEqualityComparerinterface is a convenience that allows you to implement Equals()and GetHashCode()in a separate class when you don't have access to the internals of the classes you need to compare, or if you are using a different method of comparison.

当您无法访问需要比较的类的内部结构或使用不同的比较方法时,该IEqualityComparer接口很方便,它允许您在单独的类中实现Equals()GetHashCode()

回答by Jehof

Another solution without implementing IEquatable, Equalsand GetHashCodeis to use the LINQs GroupBymethod and to select the first item from the IGrouping.

而不实施另一种解决方案IEquatableEquals并且GetHashCode是使用LINQsGroupBy方法和选择来自IGrouping的第一项。

var temp = books.SelectMany(book => book.Authors)
                .GroupBy (y => y.FirstName + y.LastName )
                .Select (y => y.First ());

foreach (var author in temp){
  Console.WriteLine(author.FirstName + " " + author.LastName);
}

回答by Alex

The Above answers are wrong!!! Distinct as stated on MSDN returns the default Equator which as stated The Default property checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation. Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T

楼上的回答错了!!!如 MSDN 所述,Distinct 返回默认 Equator,如前所述Default 属性检查类型 T 是否实现 System.IEquatable 接口,如果是,则返回使用该实现的 EqualityComparer。否则,它返回一个 EqualityComparer,它使用 T 提供的 Object.Equals 和 Object.GetHashCode 的覆盖

Which means as long as you overide Equals you are fine.

这意味着只要你覆盖 Equals 你就可以了。

The reason you're code is not working is because you check firstname==lastname.

您的代码不起作用的原因是因为您检查了名字==姓氏。

see https://msdn.microsoft.com/library/bb348436(v=vs.100).aspxand https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx

请参阅https://msdn.microsoft.com/library/bb348436(v=vs.100).aspxhttps://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx

回答by Ashu_90

There is one more way to get distinct values from list of user defined data type:

还有另一种方法可以从用户定义的数据类型列表中获取不同的值:

YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();

Surely, it will give distinct set of data

当然,它会给出不同的数据集