区别于不使用LINQ to Objects

Tan*_*moy 112 .net c# linq iequatable iequalitycomparer

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

这基于"LINQ in Action"中的示例.代码清单4.16

这两次打印Jon Skeet.为什么?我甚至尝试在Author类中重写Equals方法.仍然不同似乎没有用.我错过了什么?

编辑:我也添加了==和!=运算符重载.仍然没有帮助.

 public static bool operator ==(Author a, Author b)
    {
        return true;
    }
    public static bool operator !=(Author a, Author b)
    {
        return false;
    }
Run Code Online (Sandbox Code Playgroud)

ska*_*alb 148

LINQ Distinct在自定义对象方面并不聪明.

它所做的就是查看你的列表并看到它有两个不同的对象(它们并不关心它们对于成员字段具有相同的值).

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

如果你像这样修改你的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

尝试使用DotNetFiddle

  • IEquatable很好但不完整; 你应该*总是*一起实现Object.Equals()和Object.GetHashCode(); IEquatable <T> .Equals不会覆盖Object.Equals,因此在进行非强类型比较时会出现这种情况,这种情况通常发生在框架中,并且始终存在于非泛型集合中. (18认同)
  • @Tanmoy取决于.如果您希望Author通常表现得像普通对象(即只是引用相等),但为了Distinct的目的检查名称值,请使用IEqualityComparer.如果*总是*希望根据名称值比较Author对象,则覆盖GetHashCode和Equals,或实现IEquatable. (3认同)
  • 我实现了`IEquatable`(和overrode`Equals` /`GetHashCode`),但我的断点都没有在Linq`Distinct`上触发这些方法? (3认同)
  • @PeterX 我也注意到了这一点。我在`GetHashCode` 和`Equals` 中有断点,它们在foreach 循环中被击中。这是因为 `var temp = books.SelectMany(book =&gt; book.Authors).Distinct();` 返回一个 `IEnumerable`,意思是请求不会立即执行,只有在使用数据时才会执行. 如果您想立即获得此触发的示例,请在“.Distinct()”之后添加“.ToList()”,您将在 foreach 之前的“Equals”和“GetHashCode”中看到断点。 (2认同)

Rex*_*x M 65

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

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

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

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Jeh*_*hof 42

而不实施另一种解决方案IEquatable,Equals并且GetHashCode是使用LINQs GroupBy方法和选择来自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);
}
Run Code Online (Sandbox Code Playgroud)

  • 我更喜欢这种解决方案,但是可以通过在groupby中使用“ new”对象来实现:`.GroupBy(y =&gt; new {y.FirstName,y.LastName}) (2认同)

小智 26

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

YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();
Run Code Online (Sandbox Code Playgroud)

当然,它会提供不同的数据集


And*_*dyM 20

Distinct()对枚举中的对象执行默认的相等比较.如果你还没有覆盖Equals()GetHashCode(),然后它使用的默认实现object,这比较引用.

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

IEqualityComparer接口是方便,可让您实现Equals()GetHashCode()在一个单独的类,当你不能够访问你需要比较,或者如果您使用比较不同的方法类的内部结构.


Eri*_*ing 10

你已经覆盖了Equals(),但要确保你也覆盖了GetHashCode()


小智 7

以上答案都是错误的!MSDN上声明的区别是返回默认的Equator,如上所述,Default属性检查类型T是否实现System.IEquatable接口,如果是,则返回使用该实现的EqualityComparer.否则,它返回一个EqualityComparer,它使用由T提供的Object.Equals和Object.GetHashCode的覆盖.

这意味着只要你超越Equals就可以了.

您的代码无效的原因是您检查firstname == lastname.

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


小智 5

代替

var temp = books.SelectMany(book => book.Authors).Distinct();
Run Code Online (Sandbox Code Playgroud)

var temp = books.SelectMany(book => book.Authors).DistinctBy(f => f.Property);
Run Code Online (Sandbox Code Playgroud)