想象一下:
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缺乏经验。这是一种可行的模式吗?
答案 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
将调用在其作用域中注册的所有内容[但不是嵌套作用域],或构造函数抛出,后者是一种难以避免泄漏的情况。