Distinct 不适用于 LINQ to Objects
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;
}
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.
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?
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(11)
Distinct()
方法检查引用类型的引用相等性。这意味着它正在寻找字面上相同的重复对象,而不是包含相同值的不同对象。
有一个重载,它需要一个IEqualityComparer,因此您可以指定不同的逻辑来确定给定对象是否等于另一个对象。
如果您希望 Author 通常表现得像普通对象(即仅引用相等性),但为了通过名称值进行区分检查相等性,请使用 IEqualityComparer。如果您始终希望根据名称值比较 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);
}
Distinct()
对可枚举对象执行默认的相等比较。如果您尚未覆盖 Equals()
和 GetHashCode()
,然后它使用 object
上的默认实现来比较引用。
简单的解决方案是添加 Equals()
和 GetHashCode()
到参与您正在比较的对象图的所有类(即 Book 和 Author)。
IEqualityComparer
界面非常方便,允许您实施Equals()
< /a> 和 GetHashCode()
< /a> 在单独的类中,当您无法访问需要比较的类的内部结构,或者您正在使用不同的比较方法时。
上面的答案都是错误的!!!
正如 MSDN 上所述,Distinct 返回默认的 Equator,如所述,Default 属性检查类型 T 是否实现 System.IEquatable 接口,如果是,则返回使用该实现的 EqualityComparer。 否则,它会返回一个 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
您可以通过多种方式实现此目的:
1.您可以实现 IEquatable 接口,如下所示Enumerable.Distinct 方法 或者您可以查看 @skalb 在这篇文章中的回答
2. 如果您的对象没有唯一键,您可以使用 GroupBy 方法来获得不同的对象列表,您必须对对象的所有属性,然后选择第一个对象。
例如如下所示并为我工作:
var distinctList= list.GroupBy(x => new {
Name= x.Name,
Phone= x.Phone,
Email= x.Email,
Country= x.Country
}, y=> y)
.Select(x => x.First())
.ToList()
MyObject 类如下所示:
public class MyClass{
public string Name{get;set;}
public string Phone{get;set;}
public string Email{get;set;}
public string Country{get;set;}
}
3. 如果您的对象具有唯一键,则只能在分组依据中使用它。
例如,我的对象的唯一键是 Id。
var distinctList= list.GroupBy(x =>x.Id)
.Select(x => x.First())
.ToList()
您可以在列表上使用扩展方法,该方法根据计算的哈希检查唯一性。
您还可以更改扩展方法以支持 IEnumerable。
示例:
public class Employee{
public string Name{get;set;}
public int Age{get;set;}
}
List<Employee> employees = new List<Employee>();
employees.Add(new Employee{Name="XYZ", Age=30});
employees.Add(new Employee{Name="XYZ", Age=30});
employees = employees.Unique(); //Gives list which contains unique objects.
扩展方法:
public static class LinqExtension
{
public static List<T> Unique<T>(this List<T> input)
{
HashSet<string> uniqueHashes = new HashSet<string>();
List<T> uniqueItems = new List<T>();
input.ForEach(x =>
{
string hashCode = ComputeHash(x);
if (uniqueHashes.Contains(hashCode))
{
return;
}
uniqueHashes.Add(hashCode);
uniqueItems.Add(x);
});
return uniqueItems;
}
private static string ComputeHash<T>(T entity)
{
System.Security.Cryptography.SHA1CryptoServiceProvider sh = new System.Security.Cryptography.SHA1CryptoServiceProvider();
string input = JsonConvert.SerializeObject(entity);
byte[] originalBytes = ASCIIEncoding.Default.GetBytes(input);
byte[] encodedBytes = sh.ComputeHash(originalBytes);
return BitConverter.ToString(encodedBytes).Replace("-", "");
}
下面代码中的等于运算符不正确。
旧
public bool Equals(Author other)
{
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
新
public override bool Equals(Object obj)
{
var other = obj as Author;
if (other is null)
{
return false;
}
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
当涉及到自定义对象时,LINQ Distinct 并不那么聪明。
它所做的只是查看您的列表并查看它有两个不同的对象(它不关心它们的成员字段是否具有相同的值)。
一种解决方法是实现 IEquatable 接口,如此处所示。
如果你像这样修改你的 Author 类,它应该可以工作。
尝试使用 DotNetFiddle
LINQ Distinct is not that smart when it comes to custom objects.
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.
If you modify your Author class like so it should work.
Try it as DotNetFiddle