2010-02-23 40 views
2

我正在使用一个巨大的二叉树,其结构的节点可能会或可能不会使用非托管资源。其中一些资源可能会占用大量内存,但其中只有少数会一次被使用。树的初始状态可以被看作“休眠”。IDisposable对象意味着只创建一次吗?

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

我目前正在利用IDisposable接口来实现此目的。这非常有用,因为有很多情况下我需要创建将在本地使用的小型分支机构,并且“使用”关键字非常方便,确保不会意外打开任何资源。

我确定在没有真正处置的物体上实施IDisposable,但有些处于睡眠状态?

在此先感谢。

编辑:谢谢所有的聪明的答案。我喜欢处理资源访问的想法,而不是资源本身。现在我正在为负责清理的功能寻找一个更好的名字。 (比发行()或睡眠()?再次感谢。其他任何想法

+0

在此主题深入讨论:http://stackoverflow.com/questions/2101524/is-it-abusive-to-use-idisposable-and-using-as-a-means-for-getting-scoped- behav – 2010-02-23 11:58:27

+0

@nobugz - 我不认为这是完全相同的事情 - 尽管那个线程肯定是相关的 – philsquared 2010-02-23 12:16:56

回答

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意味着你不会得到拳击开销,相反公开的.Dispose方法只会在using -block退出时被调用。

+0

+1非常感谢您的回答。这正是我所期待的。 – Trap 2010-02-23 14:08:46

2

我会说“不”。IDisposable有一个特定的目的,而“沉睡”不是吧。

5

这不完全从IDisposable.Dispose文档,其中包括本(重点煤矿)明确:

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

也是这样:

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

后者表明它确实不应用于“复位”的操作,这是我想你了。 (我不知道你的“置于睡眠状态”术语确实有助于在这里;呢,我就在说你真的在所有子节点配置的所有主动获取的资源)

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

我可以看到你正在尝试做什么,但我不确定最好的做法......

+0

“我是否正确地说你真的在所有子节点中处理所有主动获取的资源?是的,乔恩,这正是我的意思。 – Trap 2010-02-23 12:32:37

+0

这是“摆脱沉重的负荷,但保持”逻辑上“​​完好未来用” – Trap 2010-02-23 12:38:50

1

当然,你不应该释放资源,如果你没有得到他们,所以在这种情况下,您的Dispose方法不会做任何事情。

也许,你应该使用内用IDisposable的复合对象和分配/配置资源这一属性/字段。所以你会醒来(为资源分配新的对象),并让你的节点处于活动状态,进入休眠状态(处置资源)。

在这种情况下,你的钢需要自IDisposable派生节点,因为当你有属性/字段与IDisposable接口,容器应实现IDisposable了。

1

不,这似乎并不像一个适当的使用了IDisposable的。

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

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

+1与菲尔一样​​,我喜欢这种方法,谢谢:) – Trap 2010-02-23 14:06:59

1

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

这里正在迷茫的对象寿命。一方面你有一个长寿的对象(node),它并不总是使用其他资源。另一方面,你有一些节点可能在“醒来”时使用的其他资源,并且在返回睡眠状态时(由于选择了另一个节点)将放弃(如果我对你有好处)。

所以,它听起来就像你有两个寿命的概念,而这些可以通过引入另一个目的是更直接地管理资源进行建模。因此,将你的“懒惰获得的资源”放到新的资源管理对象中,而资源管理对象本身也热切地获取资源 - 并将它们分配到dispose()中。然后,您的节点可以根据需要(在唤醒时)创建资源管理对象,并在完成时将它们处理掉(重新进入休眠状态),并且寿命不会混淆。

+0

+1这是非常有益的。事实上,我已经在使用这种'间接指向'(我通常在其中添加'访问'或'访问者'这个词),但这次我没有看到树木的森林。谢谢:) – Trap 2010-02-23 14:05:50