Distinct 不适用于 LINQ to Objects
-
21-09-2019 - |
题
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;
}
}
这是基于“LINQ in Action”中的示例。清单 4.16。
这会打印 Jon Skeet 两次。为什么?我什至尝试过重写 Author 类中的 Equals 方法。Still Distinct 似乎不起作用。我缺少什么?
编辑:我也添加了 == 和 != 运算符重载。仍然没有帮助。
public static bool operator ==(Author a, Author b)
{
return true;
}
public static bool operator !=(Author a, Author b)
{
return false;
}
解决方案
,当涉及到自定义对象LINQ鲜明不是聪明。
它所做的就是看你的清单,看看它有两个不同的对象(它并不关心,他们对成员字段的值相同)。
一个解决方法是实施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;
}
}
其他提示
这 Distinct()
方法检查引用类型的引用相等性。这意味着它正在寻找字面上相同的重复对象,而不是包含相同值的不同对象。
有一个 超载 这需要一个 IE质量比较器, ,因此您可以指定不同的逻辑来确定给定对象是否等于另一个对象。
如果您希望 Author 通常表现得像普通对象(即仅引用相等性),但为了按名称值区分检查相等性,请使用 IE质量比较器. 。如果您总是希望根据名称值比较 Author 对象,那么 覆盖 GetHashCode 和 Equals, , 或者 实施IEquatable.
两位成员在 IEqualityComparer
接口有 Equals
和 GetHashCode
. 。您确定是否两个的逻辑 Author
如果名字和姓氏字符串相同,则对象看起来相等。
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();
}
}
不实施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);
}
还有另一种方法可以从用户定义的数据类型列表中获取不同的值:
YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();
当然,它会给出一组不同的数据
Distinct()
对可枚举对象执行默认的相等比较。如果你还没有覆盖 Equals()
和 GetHashCode()
, ,然后它使用默认实现 object
, ,比较参考。
简单的解决方案是添加一个 正确的 实施 Equals()
和 GetHashCode()
到参与您正在比较的对象图的所有类(即书籍和作者)。
这 IEqualityComparer
接口是一种方便,允许您实现 Equals()
和 GetHashCode()
当您无法访问需要比较的类的内部结构,或者您正在使用不同的比较方法时,可以在单独的类中进行比较。
您已经重写了 Equals(),但请确保您也重写了 GetHashCode()
上面的答案都是错误的!!!正如 MSDN 上所述,Distinct 返回默认的 Equator,如所述 Default 属性检查类型 T 是否实现 System.IEquatable 接口,如果是,则返回使用该实现的 EqualityComparer。 否则,它返回一个 EqualityComparer,它使用 T 提供的 Object.Equals 和 Object.GetHashCode 的重写
这意味着只要你覆盖 Equals 就可以了。
您的代码不起作用的原因是您检查名字==姓氏。
看 https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx 和 https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx