关于如何避免两次处理对象的建议

时间:2012-01-11 17:03:57

标签: c# dispose

我有一个一次性物品清单,我将其添加到已包含许多一次性物品的集合中。我将代码包装在try ... finally块中,这样如果在我将列表中的项目复制到集合时抛出异常,则列表中的所有对象都将被正确处理:

private static void LoadMenuItems(ApplicationMenu parent)
{
    List<ApplicationMenuItem> items = null;
    try
    {
        items = DataContext.GetMenuItems(parent.Id);
        foreach (var item in items)
        {
            parent.Items.Add(item);
        }
        items = null;
    }
    finally
    {
        if (items != null)
        {
            foreach (var item in items)
            {
                item.Dispose();
            }
        }
    }
}

如果在将多个对象添加到集合后发生异常,我将遇到集合包含一些已处置对象的情况。这可能会导致那些被处置的物体再次被丢弃在下面的尝试...抓住块:

try
{
    // Assume that menu.Items contains some items prior
    // to the call to LoadMenuItems.
    LoadMenuItems(menu);
}
catch
{
    // The Dispose() iterates through menu.Items calling 
    // Dispose() on each.
    menu.Dispose();
}

所以我正在寻找可能的解决方案来阻止Dispose()被调用两次。我有一个解决方案,但我想我会把它交给社区,看看是否有其他选择。

2 个答案:

答案 0 :(得分:5)

  

这可能会导致那些被处置的物体再次被丢弃

哪个应该没问题。 Dispose()的合同非常具体:多次调用它应该是安全的。


但摆脱它的另一种方法是:

分析你的逻辑我会说finally部分是多余的,也许分析师也这么认为。你确实两次解决同样的问题。

答案 1 :(得分:2)

大多数情况下,人们可能会担心“意外”多次处理一个物体,因为对于“拥有”有关物体的人存在混淆,这种混淆可能会产生除重复处置之外的其他问题。虽然可以通过处置方法使用标志来避免多重处置本身导致问题,因此第二次处置尝试将无害地返回,这样做而不解决关于IDisposable所有权的混淆不会导致更严重的问题未解决。

不应将重复处置企图视为表明更广泛问题的主要方案是

  1. 使用部分构造的对象创建对象失败的情况;虽然人们可能会定义关于部分构建的对象的哪些部分负责清理哪些其他部分的策略,但是可能更容易简单地让每个部分被告知执行“紧急”清理以告知它所知道的每个部分。如果还没有这样做的话。在大多数处理场景中,关于对象所有权的混淆可能导致对象在仍在使用时被丢弃,但是如果对象工厂失败,这通常意味着没有向将要使用它们的任何人发布对该对象的引用。
  2. 处置正在使用的对象是具有可预测语义的合法使用场景的情况。例如,某些数据源具有阻塞等待方法,并且在阻塞方法等待时明确指定处理数据源将使该方法失败而不会有进一步的延迟。在某些情况下,很可能是将一次性资源从使用它的任务中剔除是这种任务变得不稳定的唯一方法。

你的场景看起来有点像第一个,除了看起来你将在将每个项目添加到parent.Items之后处理它们,这表明你可能会混淆对象所有权问题。