【问题标题】:DI: Handling Life of IDisposable ObjectsDI:处理 IDisposable 对象的生命周期
【发布时间】:2010-12-06 04:32:33
【问题描述】:

所以我正在开发我的 DI/IoC 容器 OpenNETCF.IoC,我有一个(合理的)功能请求,为容器集合中的 IDisposable 项添加某种形式的生命周期管理。

我目前的想法是,由于我无法查询对象以查看它是否已被释放,并且我无法获取它何时被释放的事件,因此我必须为对象创建某种形式的包装器开发人员希望框架进行管理。

现在可以使用 AddNew 添加对象(为简单起见,我们假设只有一个重载并且没有 Add):

public TTypeToBuild AddNew<TTypeToBuild>() { ... }

我正在考虑的是添加一个新方法(它们是一组,但你明白了):

public DisposableWrappedObject<IDisposable> AddNewDisposable<TTypeToBuild>()
    where TTypeToBuild : class, IDisposable
{
    ...
}

DisposableWrappedObject 如下所示:

public class DisposableWrappedObject<T>
    where T : class, IDisposable
{
    public bool Disposed { get; private set; }
    public T Instance { get; private set; }

    internal event EventHandler<GenericEventArgs<IDisposable>> Disposing;

    internal DisposableWrappedObject(T disposableObject)
    {
        if (disposableObject == null) throw new ArgumentNullException();

        Instance = disposableObject;
    }

    ~DisposableWrappedObject()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        lock(this)
        {
            if(Disposed) return;

            EventHandler<GenericEventArgs<IDisposable>> handler = Disposing;
            if(handler != null)
            {
                Disposing(this, new GenericEventArgs<IDisposable>(Instance));
            }

            Instance.Dispose();

            Disposed = true;
        }
    }
}

现在,当通过 AddNewDIsposable 将项目添加到容器时,还会添加一个事件处理程序,以便当它被 Disposed(通过包装器)时,框架会将其从基础集合中删除。

我实际上已经实现了这个并且它通过了单元测试,但我正在寻找关于它可能被破坏的地方的意见,或者如何使它对消费开发者更加“友好”。

编辑 1

由于有关于如何使用 Disposing 事件的问题,这里有一些代码(修剪到重要的部分):

private object AddNew(Type typeToBuild, string id, bool wrapDisposables)
{
    ....

    object instance = ObjectFactory.CreateObject(typeToBuild, m_root);

    if ((wrapDisposables) && (instance is IDisposable))
    {
        DisposableWrappedObject<IDisposable> dispInstance = new
               DisposableWrappedObject<IDisposable>(instance as IDisposable);
        dispInstance.Disposing += new 
               EventHandler<GenericEventArgs<IDisposable>>(DisposableItemHandler);
        Add(dispInstance as TItem, id, expectNullId);
        instance = dispInstance;
    }

    ....

    return instance;
}

private void DisposableItemHandler(object sender, GenericEventArgs<IDisposable> e)
{
    var key = m_items.FirstOrDefault(i => i.Value == sender).Key;
    if(key == null) return;
    m_items.Remove(key);
}

【问题讨论】:

  • 我们能否更全面地描述正在添加的特定功能?人们想要这个的用例是什么,您作为(IoC 框架)或最终用户的事件处理程序是什么?等等。
  • 用例是添加自动化生命周期管理。如果您将 IDisposable 项添加到集合并稍后调用 Dispose,则它实际上永远不会被清理,因为容器拥有对象的根。这个想法是,您可以在对象上调用 Dispose ,而无需返回集合来查找它,并让它自动导致从容器集合中删除。该事件由框架纯粹在内部使用(它甚至被标记为内部,以免在外部使用)并且处理程序将其从集合中删除。
  • 为了清楚起见,我更新了问题以添加事件处理。
  • 所以消费者已经处理掉了这个对象,他们想确保你不再持有它?如果他们再次要求会发生什么,你会做一个新的吗?就代码而言,我唯一能注意到的不寻常之处是您正在通过事件处理程序从终结器中使用 GC 线程,因此请小心该代码! :)
  • @ctacke:“如果您将 IDisposable 项添加到集合中,然后调用 Dispose,它实际上永远不会被清理,因为容器拥有对象的根。”持有引用仅适用于 GC。处置非托管资源应该是开发人员的责任。调用 Dispose 立即进行清理,而不管存在哪些其他引用。

标签: c# compact-framework dependency-injection ioc-container opennetcf.ioc


【解决方案1】:

也许我遗漏了一些东西,但为什么要向 API 添加新方法?当一个对象被添加到容器中时,你可以通过 as-cast 来检查它是否是 IDisposable 并适当地处理它。

我也想知道你是否需要析构函数。假设容器是 IDisposable 的(就像 Unity 的),你可以只实现 Basic Dispose Pattern 并节省大量的 GC 开销。

一些可能适用的问题:

【讨论】:

  • 啊,但是有了演员表,然后呢?我知道它是 IDisposable 并且必须放入容器中,但我无法返回所述容器,因为 AddNew 方法必须返回输入类型。如果我按照 API 的要求直接返回对象,他们不知道它已被包装,然后只在实例上调用 Dispose,而不是容器,我再次遇到了持有永远不会被 GC 处理的 Disposed 对象的问题。
  • @ctacke:消费者在玩 IoC 时必须遵守一些规则:当你将创建对象的责任移交给 DI 容器时,你必须移交所有生命周期管理,包括销毁实例。因此,如果消费者过早地处理注入的依赖项,这将是消费者方面的错误,因为该实例可能在多个消费者之间共享。我认为您不需要过度复杂化您的 API 以防止使用完全错误的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
  • 1970-01-01
  • 2019-02-02
  • 2011-04-28
  • 1970-01-01
相关资源
最近更新 更多