我一直在尝试添加一个新的扩展方法来区分IEnumerable<T>
对象。对于学习和应用的目的。
背后的逻辑应该这样做:(this works)
// sis is a DbContext by the way
List<MyObj> objs = sis.MyObjs.ToList();
// objs contains duplicate entities
List<MyObj> _objs = new List<MyObj>();
foreach(MyObj e in MyObjs)
{
if (_ems.Contains(e) == false) _ems.Add(e);
}
foreach(MyObj e in _ems)
{
Console.WriteLine(e.ID); // Distinction happens
}
我已经写了一个新的扩展方法来做同样的上面的行。
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> en)
{
foreach(T f in en)
{
if (en.Contains(f) == false) yield return f;
}
}
,但它没有工作。奇怪的是,我也试过(分别)
objs.Distinct(); // default Distinct() method in Linq
objs.GroupBy(t => t.ID).Select(t => t.FirstOrDefault());
但它们也不能有不同的对象。唯一有效的是,我上面写的第一个逻辑。
那么,怎么可能写一个新的扩展来做同样的事情呢?
这都取决于T
如何实现Equals
和GetHashCode
。如果实现是默认继承自object
,那么这将是引用相等。
除非T
具有像string
那样的不可变性,否则所有的实例都将是不同的,它们将有不同的引用。
你可以在你的Distinct
方法中添加一个重载来接受IEqualityComparer<T>
的实现来覆盖T
的行为。
另外,,你当前的实现更像是一个存在剥离器,参见下面提出的替代方案。
public static IEnumerable<T> Distinct<T>(
this IEnumerable<T> source,
IEqualityComparer<T> comparer = null)
{
if (comparer == null)
{
comparer = EqualityComparer<T>.Default;
}
var seen = new HashSet<T>(comparer);
foreach (var t in source)
{
if (seen.Contains(t))
{
continue;
}
yield return t;
seen.Add(t);
}
}
当使用List和dictionary时,始终记得在您希望使用的T实体中重写Equals和GetHashCode。
在上面的例子中,您比较的是引用(地址),而不是这些地址的预期值。