IDisisposable对象是否只需创建和处理一次?

时间:2010-02-23 11:52:43

标签: .net idisposable

我正在使用一个巨大的二叉树结构,其节点可能会也可能不会使用非托管资源。其中一些资源可能占用大量内存,但一次只能使用其中一些内存。树的初始状态可以被视为“休眠”。

每当访问一个节点时,该特定节点及其子节点将“唤醒”并懒洋洋地获取其分配的资源。同样,访问树中的不同分支将使当前活动的分支进入休眠状态,从而释放其资源。这意味着任何给定节点都可以在任何给定时间被唤醒并一次又一次地进入睡眠状态。

我目前正在利用IDisposable接口来实现这一目标。它非常有用,因为在很多情况下我需要创建将在本地使用的小分支,并且'using'关键字非常方便,确保不会意外地保留资源。

我是否可以在对象上实现IDisposable,而这些对象并没有真正被处理掉但又有点睡觉?

提前致谢。

编辑:感谢所有聪明的答案。我喜欢处理资源访问而不是资源本身的想法。现在我正在为负责清理的功能寻找更好的名称。 (除了Release()或Sleep()之外的任何想法?再次感谢。

6 个答案:

答案 0 :(得分:5)

IDisposable.Dispose文档中并不完全清楚,其中包括(强调我的):

  

执行与释放,发布,或重置非托管资源相关的应用程序定义任务。

但也是这样:

  

如果对象的Dispose方法是   不止一次地召唤,对象必须   忽略第一个之后的所有呼叫。   该对象不得抛出异常   如果调用其Dispose方法   多次。其他实例方法   比Dispose可以抛出一个   资源时ObjectDisposedException   已被处置。

后者暗示它确实不应用于“重置”操作,这是我认为你正在追求的。 (我不确定你的“睡觉”术语在这里真的有用;我说你真的在处理所有子节点中所有积极获取的资源了吗?)

当然,这只是按照惯例 - 你可以让你的代码做你喜欢的任何事情。但是,我认为大多数开发人员会觉得有些奇怪。

我可以看到你想要做什么,但我不确定这样做的最佳方式......

答案 1 :(得分:4)

@Jon Skeetanswered the提出的问题非常好,但请允许我填写评论,我觉得应该是自己的答案。

使用using代码块来临时获取某些资源,或者输入某种形式的范围代码是非常常见的。我一直这样做,特别是在我的业务逻辑控制器中,我有一个系统推迟更改事件,直到执行一段代码,避免副作用多次,或者在我准备好它们之前,等

为了使代码对使用它的程序员看起来更加明显,您应该看一下使用use而不是具有该资源的对象的临时值,并从方法名称返回它告诉程序员它在做什么,即。暂时获得一些资源。

让我举个例子。

而不是:

using (node) { ... }

你这样做:

using (node.ResourceScope()) { ... }

因此,您实际上并没有多次处理任何内容,因为ResourceScope将返回您处理的新值,而基础节点将保持原样。

这是一个示例实现(未验证,从内存中键入):

public class Node
{
    private Resource _Resource;

    public void AcquireResource()
    {
        if (_Resource == null)
            _Resource = InternalAcquireResource();
    }

    public void ReleaseResource()
    {
        if (_Resource != null)
        {
            InternalReleaseResource();
            _Resource = null;
        }
    }

    public ResourceScopeValue ResourceScope()
    {
        if (_Resource == null)
            return new ResourceScopeValue(this);
        else
            return new ResourceScopeValue(null);
    }

    public struct ResourceScopeValue : IDisposable
    {
        private Node _Node;

        internal ResourceScopeValue(Node node)
        {
            _Node = node;
            if (node != null)
                node.AcquireResource();
        }

        public void Dispose()
        {
            Node node = _Node;
            _Node = null;
            if (node != null)
                node.ReleaseResource();
        }
    }
}

这允许您这样做:

Node node = ...
using (node.ResourceScope())     // first call, acquire resource
{
    CallSomeMethod(node);
}                                // and release it here

...
private void CallSomeMethod(Node node)
{
    using (node.ResourceScope()) // due to the code, resources will not be 2x acquired
    {
    }                            // nor released here
}

我返回一个结构而不是IDisposable的事实意味着你不会得到拳击开销,而只是在从using - 块退出时调用公共.Dispose方法。

答案 2 :(得分:2)

我会说,“不”。 IDisposable有一个特殊目的,“睡觉”不是它。

答案 3 :(得分:1)

当然,如果你没有获得资源,就不应该释放资源,所以在这种情况下你的Dispose方法什么都不做。

也许,您应该在内部使用IDisposable的复合对象,并在此Property / Field中分配/处置资源。因此,您将在节点处于活动状态时醒来(使用资源分配新对象)并进入休眠状态(处置资源)。

在这种情况下,你需要从IDisposable派生你的节点,因为当你有IDisposable的属性/字段时,容器也应该实现IDisposable。

答案 4 :(得分:1)

不,这似乎不适合使用IDisposable。

快速思考你能做什么;实现另一个IDisposable对象,该对象可以包含加载的数据,并从对象的方法调用返回该数据;例如:

using(var wokenUpData = dataContainer.WakeUp())
{
    // access the data using wokenUpData
    ...
}

答案 5 :(得分:1)

听起来你只需要添加额外的level of indirection

这里混淆的是对象生命周期。一方面,你有一个长寿命的对象(node),它并不总是使用其他资源。另一方面,你有一些节点在“清醒”时可以使用的其他资源,并且在返回睡眠时(由于选择了另一个节点)将放弃(如果我帮你正确)。

所以听起来你有两个终身概念,这些可以通过引入另一个对象来更直接地管理资源来建模。

因此,将您的“懒惰获取的资源”放入新的资源管理对象中,该对象本身急切地获取资源 - 并在dispose()中处理它们。然后你的节点可以根据需要创建资源管理对象(醒来时),并在完成后处理它们(回到睡眠状态) - 并且生命周期不会混淆。