“Access to disposed closure”是什么意思?

8
我有以下代码:
public void DequeueRecipe(AuthIdentity identity, params Guid[] recipeIds)
{
   using (var session = GetSession())
   {
      var recipes = (from r in recipeIds select new Models.Recipes {RecipeId = r}).ToArray();

      var dbRecipes = session.QueryOver<Models.QueuedRecipes>()
         .Where(Expression.Eq("UserId", identity.UserId))
         .Where(Expression.InG("Recipe", recipes))
         .List<Models.QueuedRecipes>();

      using (ITransaction transaction = session.BeginTransaction())
      {
         dbRecipes.ForEach(r => session.Delete(r)); // <-- Warning
         transaction.Commit();
      }
   }
}

ReSharper给我警告:

访问已释放的闭包

在这一行上:

dbRecipes.ForEach(r => session.Delete(r));

session变量被强调为罪魁祸首)。

虽然 ForEach 方法接受一个 lambda 表达式,该表达式围绕变量 session 创建了一个闭包,但我不认为在执行此代码时它会被释放。也许 reSharper 认为 ForEach 可能会并行执行某些任务,或者将那个 Action<> 保存到稍后的时间,因此在匿名函数仍然可以访问内存时可能会被释放。

我可以无视这个警告吗?有没有办法重新格式化我的代码以防止出现这个警告?这段代码确实存在危险吗?

我习惯于 reSharper 比我更聪明,所以我想确切地了解发生了什么。


与此无关:事务的开始/提交难道不是Delete()的职责吗?在事务之外执行Delete()是否可能/正确(假设它有多个操作[表])? - Mitch Wheat
同意,我可以删除事务,因为当会话被处理时,所有内容都将被提交。我一直在重新调整我的会话代码,所以这是我早期架构的遗留物。 - Mike Christensen
当我使用这个批处理代码片段将IEnumerable分组成大小为x的批次时,我也遇到了这个错误:https://dev59.com/42Yr5IYBdhLWcg3wXJEK#44505349 我的老板看到Resharper的警告后非常生气。但是据我所知,这是完全安全的代码。 - John Zabroski
1个回答

11
因为session被封装在一个using语句中,而LINQ执行可能会被延迟直到它被枚举。
Resharper警告可能会导致异常,因为当枚举dbRecipes时,session可能已经被处理掉。
老实说:我不确定以上代码是否会按照警告的方式失败。

2
ForEach 应立即枚举 dbRecipes,是吗?因此不应该有任何推迟。 - Mike Christensen
你不认为ReSharper的警告是错误的吗?在那段代码中,session真的会被处理吗?我从来没有见过我的代码表现出这种行为。根据我对using语句的理解,ReSharper是错的。 - evanmcdonnal
1
如果您在评估dbrecipes时添加.ToList()会发生什么? - Mitch Wheat
1
如果我这样做,警告就会消失。因此,ReSharper 足够聪明,知道该列表在那个时候肯定被枚举了,但不足以看出 dbRecipes 是一个 Guid[],因此枚举无论如何都不能被延迟。 - Mike Christensen
我认为这只是谨慎的一面。另外,它知道.List()是什么吗? - Mitch Wheat
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接