我玩LINQ来了解它,但我不知道如何使用鲜明当我没有一个简单的列表(一个简单的整数列表是很容易做到的,这不是问题)。如果我想使用鲜明的列表<TElement>上的一个或多个属性的TElement?
示例:如果一个对象是Person,具有属性Id。我怎么能得到所有人,并使用鲜明对他们与对象的属性Id ?
Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"
如何得到Person1和Person3?这可能吗?
如果用LINQ是不可能的,那么根据Person的某些属性获得Person列表的最佳方法是什么?
编辑:这现在是MoreLINQ的一部分。
你需要的是一个有效的“区别”。我不相信它是LINQ的一部分,尽管它很容易编写:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
因此,要使用Id属性查找不同的值,您可以使用:
var query = people.DistinctBy(p => p.Id);
要使用多个属性,你可以使用匿名类型,它可以适当地实现相等:
var query = people.DistinctBy(p => new { p.Id, p.Name });
未经测试,但应该可以工作(现在至少可以编译)。
它假设键的默认比较器-如果你想传入一个相等比较器,只需将它传递给HashSet构造函数。
当我们在项目中面临这样的任务时,我们定义了一个小API来组合比较器。
所以,用例是这样的:
var wordComparer = KeyEqualityComparer.Null<Word>().
ThenBy(item => item.Text).
ThenBy(item => item.LangID);
...
source.Select(...).Distinct(wordComparer);
API本身是这样的:
using System;
using System.Collections;
using System.Collections.Generic;
public static class KeyEqualityComparer
{
public static IEqualityComparer<T> Null<T>()
{
return null;
}
public static IEqualityComparer<T> EqualityComparerBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keyFunc)
{
return new KeyEqualityComparer<T, K>(keyFunc);
}
public static KeyEqualityComparer<T, K> ThenBy<T, K>(
this IEqualityComparer<T> equalityComparer,
Func<T, K> keyFunc)
{
return new KeyEqualityComparer<T, K>(keyFunc, equalityComparer);
}
}
public struct KeyEqualityComparer<T, K>: IEqualityComparer<T>
{
public KeyEqualityComparer(
Func<T, K> keyFunc,
IEqualityComparer<T> equalityComparer = null)
{
KeyFunc = keyFunc;
EqualityComparer = equalityComparer;
}
public bool Equals(T x, T y)
{
return ((EqualityComparer == null) || EqualityComparer.Equals(x, y)) &&
EqualityComparer<K>.Default.Equals(KeyFunc(x), KeyFunc(y));
}
public int GetHashCode(T obj)
{
var hash = EqualityComparer<K>.Default.GetHashCode(KeyFunc(obj));
if (EqualityComparer != null)
{
var hash2 = EqualityComparer.GetHashCode(obj);
hash ^= (hash2 << 5) + hash2;
}
return hash;
}
public readonly Func<T, K> KeyFunc;
public readonly IEqualityComparer<T> EqualityComparer;
}
更多细节请访问我们的网站:iequalitycompararer in LINQ。
编辑:这现在是MoreLINQ的一部分。
你需要的是一个有效的“区别”。我不相信它是LINQ的一部分,尽管它很容易编写:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
因此,要使用Id属性查找不同的值,您可以使用:
var query = people.DistinctBy(p => p.Id);
要使用多个属性,你可以使用匿名类型,它可以适当地实现相等:
var query = people.DistinctBy(p => new { p.Id, p.Name });
未经测试,但应该可以工作(现在至少可以编译)。
它假设键的默认比较器-如果你想传入一个相等比较器,只需将它传递给HashSet构造函数。
我写了一篇文章,解释了如何扩展Distinct函数,这样你就可以这样做:
var people = new List<Person>();
people.Add(new Person(1, "a", "b"));
people.Add(new Person(2, "c", "d"));
people.Add(new Person(1, "a", "b"));
foreach (var person in people.Distinct(p => p.ID))
// Do stuff with unique list here.
这是一篇文章(现在在Web档案中):扩展LINQ -在Distinct函数中指定属性
我个人使用以下类:
public class LambdaEqualityComparer<TSource, TDest> :
IEqualityComparer<TSource>
{
private Func<TSource, TDest> _selector;
public LambdaEqualityComparer(Func<TSource, TDest> selector)
{
_selector = selector;
}
public bool Equals(TSource obj, TSource other)
{
return _selector(obj).Equals(_selector(other));
}
public int GetHashCode(TSource obj)
{
return _selector(obj).GetHashCode();
}
}
然后,一个扩展方法:
public static IEnumerable<TSource> Distinct<TSource, TCompare>(
this IEnumerable<TSource> source, Func<TSource, TCompare> selector)
{
return source.Distinct(new LambdaEqualityComparer<TSource, TCompare>(selector));
}
最后,预期用途:
var dates = new List<DateTime>() { /* ... */ }
var distinctYears = dates.Distinct(date => date.Year);
我发现使用这种方法的优点是可以为其他接受IEqualityComparer的方法重用LambdaEqualityComparer类。(哦,我把yield的东西留给最初的LINQ实现…)