IEnumerable <T>的新非重复扩展方法



我一直在尝试添加一个新的扩展方法来区分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如何实现EqualsGetHashCode。如果实现是默认继承自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。

在上面的例子中,您比较的是引用(地址),而不是这些地址的预期值。

相关内容

  • 没有找到相关文章

最新更新