4

我实现了这个扩展方法来同步另一个可以是不同类型的集合:

public static void Synchronize<TFirst, TSecond>(
    this ICollection<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, bool> match,
    Func<TSecond, TFirst> create)
{
    var secondCollection = second.ToArray();

    var toAdd = secondCollection.Where(item => !first.Any(x => match(x, item))).Select(create);

    foreach (var item in toAdd)
    {
        first.Add(item);
    }

    var toRemove = first.Where(item => !secondCollection.Any(x => match(item, x))).ToArray();

    foreach (var item in toRemove)
    {
        first.Remove(item);
    }
}

ReSharper 给了我两个“隐式捕获的闭包”,一个在第一个 Where 和一个在第二个,有没有办法解决它?我找不到一个。

[更新]

根据 Eric 的观察,我编写了一个比使用 equals 函数更快的版本,而是使用哈希:

public static void Synchronize<TFirst, TSecond>(
    this ICollection<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TSecond, TFirst> convert,
    Func<TFirst, int> firstHash = null,
    Func<TSecond, int> secondHash = null)
{
    if (firstHash == null)
    {
        firstHash = x => x.GetHashCode();
    }

    if (secondHash == null)
    {
        secondHash = x => x.GetHashCode();
    }

    var firstCollection = first.ToDictionary(x => firstHash(x), x => x);
    var secondCollection = second.ToDictionary(x => secondHash(x), x => x);

    var toAdd = secondCollection.Where(item => firstCollection.All(x => x.Key != item.Key)).Select(x => convert(x.Value));

    foreach (var item in toAdd)
    {
        first.Add(item);
    }

    var toRemove = firstCollection.Where(item => secondCollection.All(x => x.Key != item.Key));

    foreach (var item in toRemove)
    {
        first.Remove(item.Value);
    }
}
4

2 回答 2

16

首先,让我描述一下 Resharper 试图提醒您注意的问题。假设你有:

Action M(Expensive expensive, Cheap cheap)
{
    Action shortLived = ()=>{DoSomething(expensive);};
    DoSomethingElse(shortLived);
    Action longLived = ()=>{DoAnotherThing(cheap);};
    return longLived;
}

这里的问题是,在 C#(以及 VB、JScript 和许多其他语言)中,两者的生命周期延长 以匹配和. 因此,即使不使用,昂贵的资源也永远不会被垃圾收集,直到死亡。cheapexpensive shortLivedlongLivedlongLivedexpensivelongLived

你的程序符合这个模式;你用两个 lambdas 做两个代表;其中一个使用first,另一个不使用。因此first,只要两个代表中较长的一个,就会存活下来。

其次,让我在这里批评 Resharper。这显然是一个误报。为了使这成为一个真正的积极的代表之一必须长寿。在这种情况下,当方法返回时,两个委托都有资格被收集;两者都是短暂的,因此这里没有实际的错误。Resharper 可以跟踪返回的查询对象Where并注意到它们在该方法中不存在。

第三,你应该怎么做?我倾向于对此无动于衷;如果代码按您喜欢的方式工作,请继续工作。我不会担心 Resharper 警告;我会更加担心您的代码对于大型集合的效率极低这一事实。如果这两个集合有 n 和 m 项,则该程序执行 O(nm) 操作。

于 2013-06-13T19:29:02.137 回答
-1

Secondcollection 变量用于两个地方,这是实际原因。使第一个表达式使用变量 'second' 而不是 secondcollection。问题在http://sparethought.wordpress.com/2012/08/17/implicit-closures-in-c-lambdas/中有更好的解释

于 2013-06-13T16:50:12.720 回答