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

本文关键字:闭包 集合 ReSharper 同步 | 更新日期: 2023-09-27 18:18:09

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

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给我两个"隐式捕获闭包",一个在第一个地方,一个在第二个,有办法修复它吗?我找不到。

(更新)

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

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

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

Action M(Expensive expensive, Cheap cheap)
{
    Action shortLived = ()=>{DoSomething(expensive);};
    DoSomethingElse(shortLived);
    Action longLived = ()=>{DoAnotherThing(cheap);};
    return longLived;
}
这里的问题是,在c#(和VB, JScript和许多其他语言)中, cheapexpensive的生存期被扩展到匹配 shortLivedlongLived的生存期。因此,即使longLived不使用expensive,在longLived死亡之前,昂贵的资源也不会被垃圾收集。

你的程序匹配这个模式;你从两个lambda中得到两个委托;其中一个使用first,另一个不使用。因此,first将在两个代表中存活的时间最长。

第二,让我批评一下Resharper。这显然是假阳性。为了使这是一个真正的正,其中一个代表必须是长寿的。在这种情况下,当方法返回时,两个委托都有资格进行收集;两者都是短暂的,因此这里没有真正的bug。Resharper可以跟踪Where返回的查询对象,并注意到它们不能在方法中存活。

第三,你应该怎么做?我倾向于什么也不做;如果代码按照你喜欢的方式工作,那么让它继续工作。我不会担心Resharper的警告;我更关心的是,对于大型集合,您的代码效率极低。如果两个集合分别有n和m个元素,则该程序执行O(nm)次操作。

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

中有更好的解释