代码之家  ›  专栏  ›  技术社区  ›  Jan

一次性注册:好模式?

  •  1
  • Jan  · 技术社区  · 16 年前

    想象一下:

    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());
        }
    }
    

    我对我的工作有点缺乏经验。这是一个可行的模式吗?

    6 回复  |  直到 13 年前
        1
  •  0
  •   Igor Zevaka    16 年前

    表面上,当您不能使释放线程安全时(例如,当与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
        }
    }
    

    让我再次重申,上述方法仅适用于某类对象。您不希望以这种模糊的方式处理您的网络连接,但是您可以为需要网络连接来处理自己的业务对象这样做。即便如此,只有当这种连接在应用程序的整个生命周期中持续存在时,这才是合适的。

        2
  •  3
  •   Adam Robinson    16 年前

    根据我的理解,您创建的组件将使用(我假设)实现 IDisposable 你只是在寻找一种方法来维护 不可分的 组件中的对象,只需遍历列表而不是调用 Dispose 在每一个项目上按名称,这是正确的吗?

    如果是这样,那就没什么问题了。但是,您的列表不应该是 static ,应为每个实例。

        3
  •  1
  •   Tim Lloyd    16 年前

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

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

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

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

    但是,对处置有一个很好的理解总是一件好事。

        4
  •  1
  •   Hans Passant    16 年前

    您的工作假设是,一个外部类对对象的生存期有特殊的了解,因此它在正确的时间调用Dispose()。那 很少地 有效,只有客户机代码知道何时处理对象。

    通过编写您的类,您实现了完全相反的目标,您将使对象存活的时间比需要的时间长得多。甚至垃圾收集器都不能运行终结器,因为您一直保持对对象的依赖,直到某个神奇的时刻 全部的 对象已准备好释放。通常的术语是“内存泄漏”。不要这样做。

        5
  •  0
  •   Peter Bromberg    16 年前

    您的代码意味着重量级选手可以实现IDisposable。因此,您所需要做的就是在应用程序结束事件处理程序中调用它的Dispose方法。

        6
  •  0
  •   supercat    13 年前

    处置注册表有时作为一个具体的类型是有用的,在这种情况下,人们知道一个正在创建的对象将有一个绑定到另一个对象的生存期。如果一个类的设计使得它的创建只能包装在一个特定的工厂方法中,并且如果不介意使用 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中甚至更好(不需要线程静态变量,处置注册方法可以是基类型的成员)。可能有点太恶心,不值得用C_,但在vb.net中,它可能是一个很好的模式。注意,如果构造函数被正确包装, Dispose 将调用在其作用域[但不是嵌套作用域]中注册的所有内容,当 处置 在课堂上被调用,或 构造函数引发 ,后者是一种在其他方面难以避免泄漏的情况。

    推荐文章