2010-02-13 53 views
4

以下代码是否使using(...)功能/用途无关?
是否会导致GC性能不足?使用`使用(...)`有效无效和/或效率低下?

class Program 
{ 
    static Dictionary<string , DisposableClass> Disposables 
    { 
     get 
     { 
      if (disposables == null) 
       disposables = new Dictionary<string , DisposableClass>(); 

      return disposables; 
     } 
    } 

    static Dictionary<string , DisposableClass> disposables; 

    static void Main(string[] args) 
    { 
     DisposableClass disposable; 
     using (disposable = new DisposableClass()) 
     { 
      // do some work 
      disposable.Name = "SuperDisposable"; 
      Disposables["uniqueID" + Disposables.Count] = disposable; 
     } 

     Console.WriteLine("Output: " + Disposables["uniqueID0"].Name); 

     Console.ReadLine(); 
    } 
} 

class DisposableClass : IDisposable 
{ 
    internal string Name 
    { 
     get { return myName; } 
     set { myName = value; } 
    } 

    private string myName; 

    public void Dispose() 
    { 
     //throw new NotImplementedException(); 
    } 
} 

输出:SuperDisposable

我的using(...)功能的理解是立即强制处置DisposableClass的。然而,在代码块中,我们将该类添加到字典集合中。我的理解是,一个类本质上是一个参考类型。所以我的实验是看看以这种方式添加到集合中的一次性对象会发生什么。

在这种情况下DisposableClass仍然很活跃。类是一个引用类型 - 所以我的假设变成了这个集合不是简单地引用这个类型,而是确实把类作为一个值。但是,这也没有意义。

那么究竟发生了什么?

编辑:修改后的代码与输出,以证明对象没有死,可能会提出一些答案。

2日编辑:这是什么归结为我通过一些更多的代码去是这样的:

public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool dispose) 
    { 
     if (!isDisposed) 
     { 
      if (dispose) 
      { 
       // clean up managed objects 
      } 

      // clean up unmanaged objects 
      isDisposed = true; 
     } 
    } 

    ~DisposableClass() 
    { Dispose(false); } 

逐步执行代码(曾在private void Dispose(bool dispose)断点),其中false传递给方法,资源在这里得到妥善处理变得势在必行。无论如何,这个班还活着,但你绝对是为了例外而设。答案让我更加好奇......

回答

11

处置对象不会破坏它;它只是告诉它清理它使用的所有非托管资源,因为它们不再需要。在你的例子中,你正在创建一个可处理的对象,分配给一个字典,然后告诉它去除一些资源。

using声明的正确方案是当您要初始化资源,对它做某些事情,然后销毁它并忘记它;例如:

using (var stream = new FileStream("some-file.txt")) 
using (var reader = new StreamReader(stream)) 
{ 
    Console.Write(reader.ReadToEnd()); 
} 

如果你想保留的对象,你用过之后,你不应该配置它,因此不应该使用using声明。

+0

不够公平。我试图理解发生的事情。如果该对象被假定被丢弃,或与该对象相关的资源。 +1 – IAbstract 2010-02-13 23:27:55

2

你不应该在这种情况下使用using块,因为你所需要的对象块结束后。只有当对象的生命周期有明确的起点和终点时才能使用它。

1

IDisposable接口指示型管理某种资源。该Dispose方法存在是为了让你处置由一个实例使用的资源,而不必等待垃圾回收发生和资源,由一个终结释放。

在您的示例中,字典仍包含对可丢弃类的引用,但实例将在using块的末尾处理。后续尝试调用实例上的方法现在可能会抛出ObjectDisposedExceptionInvalidOperationException,以指示实例不再处于“工作”状态。

处理IDisposable不会与释放占用该实例的内存或调用任何垃圾收集例程相混淆。实例由垃圾收集器跟踪和管理,只在垃圾收集器决定它时才被释放。

+0

@编程英雄:第二段是部分正确的......它取决于资源的正确处置,以及是否有任何班级成员使用这些处置的资源。它似乎仍然是一个奇怪的行为。如果你编辑它,我会删除倒票。 ;) – IAbstract 2010-02-13 23:54:26

+0

处置实例的行为在很大程度上是未定义的;期望的是假设一个实例一旦处置就不再正常运行,因为它所依赖的一些资源已经被释放。很可能某些属性仍会正常返回值,但大多数方法应该会失败。 – 2010-02-14 21:37:02

2

重要的是要记住,IDisposable虽然是一个稍微特殊的接口,但它仍然是一个接口。当使用块退出时,它会在您的对象上调用Dispose()。而已。你的引用仍然有效,如果你的Dispose方法什么都不做,你的对象将完全不受影响。如果你没有跟踪处理并明确地抛出异常,那么在这之后你将不会得到任何异常,因为在.NET中没有固有的处置状态。

+0

+1 @尼克:关于“IDispose”的好处。 – IAbstract 2010-02-14 00:04:04