一次性注册表:良好的模式?

时间:2010-05-23 20:25:07

标签: c# idisposable

想象一下:

public class Global : IDisposable 
{
    private static readonly List<IDisposable> Disposables = new List<IDisposable>();

    public void ApplicationStart()
    {
        var heavyLifter = new HeavyLifter();
        Disposables.Add(heavyLifter);
            // register a few more
    }

    public void Dispose()
    {
        Disposables.ForEach(d => d.Dispose());
    }
}

我对IDisposable缺乏经验。这是一种可行的模式吗?

6 个答案:

答案 0 :(得分:3)

根据我的理解,您正在创建一个将使用(我假设)实现IDisposable的多个资源的组件,而您只是在寻找维护IDisposable列表的方法。组件中的对象只是迭代列表而不是按名称调用每个项目上的Dispose,这是正确的吗?

如果是这样,那就没有错。但是,您的列表不应该是static,而应该是每个实例。

答案 1 :(得分:1)

在一个级别上,像Unity这样的IOC容器可以提供这种功能:

http://msdn.microsoft.com/en-us/library/ff663144.aspx

您将创建对象的职责委托给IOC容器,并可以指定LifetimeMangement选项。这可以包括在容器中注册以便以后处理,例如在应用程序关闭或表单关闭时调用容器上的处理。

对于短期对象,您可能希望自己管理处理,但对于寿命较长的对象,具有对象处理的IOC类型存储库模式可以很好地工作。对我来说效果很好。 :)

对Disposal的充分理解总是一件好事。

答案 2 :(得分:1)

您正在假设外部类具有对象生命周期的特殊知识,以便它在正确的时间调用Dispose()。 很少工作,只有客户端代码知道它何时完成了一个对象。

在你的课程编写完成后,你实现了完全相反的目标,你将使对象保持活动的时间超过必要的时间。甚至垃圾收集器都不能运行终结器,因为你一直保持着对象,直到所有对象准备好处理的神奇时刻。通常的术语是“内存泄漏”。不要这样做。

答案 3 :(得分:0)

您的代码暗示HeavyLifter实现了IDisposable。所以你需要做的就是在Application_End事件处理程序中调用它的Dispose方法。

答案 4 :(得分:0)

表面上看,当你无法使处理线程安全时(例如与COM对象交谈时,必须在同一个线程上释放),这种方法才有意义。然而,在实践中,你会发现这种方法导致物体的寿命比它们应该的长。

我会努力使处理线程安全,这样你就可以从终结器调用Dispose并实现真正的自动生命周期管理。您必须小心,因为可能不适合某些类型的资源 - 例如文件或网络句柄,这可能需要更严格的控制。否则,这是解决此问题的最佳解决方案。

如果处置在同一个线程上,那么你有点腌渍。如果需要处置的对象在模型层中(如在业务规则对象中) - 很可能它上面有UI层,需要复杂的逻辑来处理它(就像窗口关闭后的事件一样)。这是一个输/输的情况,可以选择永远存在的物体(如原始解决方案)和复杂的处理逻辑(很快就会变得难看)。

也许可以试验一下。您可以将一次性资源分离到自己的类中,并让工厂保持对它的强引用。资源对象将弱引用维护回业务对象。最终确定业务对象后,WeakReference将返回null,从而使您能够通过一次性用品并转储不再需要的那些。

public class Global {
    private static readonly List<Resource> Disposables = new List<Resource>();

    public HeavyLifter GetHeavyLifter()
    {
        var resource = new HeavyLifterResource();
        var heavyLifter = new HeavyLifter(resource);
        resource.BusinessObject = heavyLifter;
        Disposables.Add(resource);
    }

    public void DisposeAll()
    {
        Disposables.ForEach(d => d.CleanUp());
    }
}

public abstract class Resource : IDisposable {

    WeakReference<object> m_BusinessObject;
    public WeakReference<object> BusinessObject {get;set;}

    public CleanUp() {
      if (!m_BusinessObject.IsAlive)
        Dispose();
    }
}

public HeavyLifter {
    public HeavyLifter (Disposable d) {
      m_resourceObj = d;
    }

    HeavyLifterResource m_resourceObj;
}

public class HeavyLifterResource :Resource {
    public void Dispose() {
      //disposal
    }
}

让我再次重申,上述方法仅适用于某类物体。您不希望以这种模糊的方式处理网络连接,但是,例如,您可以为需要网络连接来处置自己的业务对象执行此操作。即使这样,只有当这种连接在应用程序的生命周期内持久存在时才适用。

答案 5 :(得分:0)

处理注册表有时可以作为具体类型使用,如果知道正在创建的对象将具有与另一个对象相关联的生命周期。如果一个类被设计成只能在特定的工厂方法中包含它的创建,并且如果不介意使用ThreadStatic变量,那么甚至可以替换:

DisposableThing Foo;  // At one point in the class
...
Foo  = new DisposableThing();  // In the constructor
...
DisposableThing.Dispose;  // In `Dispose(bool)`

DisposableThing = DisposeProtector.Register(new DisposableThing());

处理声明,初始化和清理,所有这些都在同一行。

一个非常漂亮的干净模式,以及在vb.net中更好的模式(不需要ThreadStatic变量,并且dispos-registration方法可以是基类型的成员)。可能有点太icky在C#中不值得,但在vb.net中它可能是一个很好的模式。请注意,如果构造函数被正确包装,那么当在类上调用Dispose时,Dispose将调用在其作用域中注册的所有内容[但不是嵌套作用域],或构造函数抛出,后者是一种难以避免泄漏的情况。

相关问题