我实现了这个扩展方法来同步一个集合与另一个不同类型的集合:
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);
}
}
最佳答案
首先让我描述一下 Resharper 试图提醒您注意的问题。假设你有:
Action M(Expensive expensive, Cheap cheap)
{
Action shortLived = ()=>{DoSomething(expensive);};
DoSomethingElse(shortLived);
Action longLived = ()=>{DoAnotherThing(cheap);};
return longLived;
}
这里的问题是,在 C#(以及 VB、JScript 和许多其他语言)中,两者 便宜
和昂贵
的生命周期被扩展以匹配 both shortLived
和 longLived
的生命周期。所以即使 longLived
不使用 expensive
,昂贵的资源也不会被垃圾回收,直到 longLived
结束。
你的程序匹配这个模式;您从两个 lambda 表达式中创建了两个代表;其中一个使用 first
而另一个不使用。因此,first
将在两个代表中的时间较长时存活。
其次,让我在这里批评一下 Resharper。这显然是误报。为了使其成为真正的积极因素,其中一位代表必须长寿。在这种情况下,当方法返回时,两个委托(delegate)都有资格被收集;两者都是短暂的,因此这里没有实际的错误。 Resharper 可以跟踪 Where
返回的查询对象,并注意到它们无法在该方法中存活。
第三,你应该怎么办?我会倾向于对此无所作为;如果代码按您喜欢的方式运行,则继续运行。我不会担心 Resharper 警告;我会非常非常担心这样一个事实,即您的代码对于大型集合而言效率极低。如果这两个集合有 n 和 m 个项目,那么这个程序执行 O(nm) 操作。
关于c# - 同步集合 : ReSharper "Implicitly captured closure",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17091375/