同步集合:ReSharper“隐式捕获闭包”

时间:2013-06-13 15:43:49

标签: c# collections closures resharper

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

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);
    }
}

2 个答案:

答案 0 :(得分:16)

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

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

这里的问题是在C#(以及VB,JScript和许多其他语言)中, cheapexpensive的生命周期都会延长以匹配生命周期of shortLivedlongLived。所以即使longLived不使用expensive,也会发生这种情况,在longLived死亡之前,昂贵的资源永远不会被垃圾收集。

您的计划符合此模式;你用两个lambdas做两个代表;其中一个使用first而另一个则不使用first。因此Where只要两个代表中的较长时间就会存在。

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

第三,你应该怎么做呢?我倾向于什么都不做;如果代码按照您的喜好工作,那么请继续工作。我不会担心Resharper的警告;我会更加关注你的代码对于大型集合来说非常低效这一事实。如果两个集合具有n和m项,则该程序执行O(nm)操作。

答案 1 :(得分:-1)

Secondcollection变量用于两个实际原因的位置。使第一个表达式使用变量'second'而不是secondcollection。在http://sparethought.wordpress.com/2012/08/17/implicit-closures-in-c-lambdas/

中可以更好地解释问题