什么时候需要处理?

时间:2009-07-30 21:46:21

标签: c# .net gdi+ dispose garbage-collection

如果您有以下代码:

Bitmap bmp = new Bitmap ( 100, 100 );
Graphics g = Graphics.FromImage ( bmp );

Pen p = new Pen ( Color.FromArgb ( 128, Color.Blue ), 1 );
Brush b = new SolidBrush ( Color.FromArgb ( 128, Color.Blue ) );

g.FillEllipse ( b, 0, 0, 99, 99 );    
g.FillRegion ( b, pictureBox1.Region );

pictureBox1.BackColor = Color.Transparent;
pictureBox1.Image = bmp;

你必须丢弃笔和刷子吗?那么bmp和g呢?

我的主要问题是,如果要手动处理这些问题,为什么他们一旦离开范围就不会被处置?如果您没有手动处理它们会发生什么?这是人们手动执行此操作的延迟吗?

6 个答案:

答案 0 :(得分:17)

是的,您必须处理它们 - 不仅仅是笔和画笔,还有BitmapGraphics

当它们超出范围时它们不被处理,因为变量本身是引用而不是对象,并且C#编译器不知道所有权是否仍然属于那些引用(例如FillEllipse理论上,它可以记住它给出的引用,并尝试在稍后使用它 - 请记住语言编译器没有任何关于库语义的特殊知识!)。

如果您想表明所有权仅限于该范围,请使用using声明:

using (Bitmap bmp = new Bitmap ( 100, 100 ))
using (Graphics g = Graphics.FromImage ( bmp ))
using (Pen p = new Pen ( Color.FromArgb ( 128, Color.Blue ), 1 ))
using (Brush b = new SolidBrush ( Color.FromArgb ( 128, Color.Blue ) ))
{
    g.FillEllipse ( b, 0, 0, 99, 99 );    
    g.FillRegion ( b, pictureBox1.Region );
}

这将使编译器根据需要自动插入对Dispose的调用,确保在保留相应的using范围后处理所有对象(通常是通过{{1}等控制传输}或return,或例外)。

如果你来自C ++背景,C#中的break直接类似于using,除了它是这里的语言构造,并且只能用于局部变量(即不用于类字段)。

答案 1 :(得分:6)

我知道其他人已经把代码示例放在这里了,但是我开始了,所以我会完成:

using (Bitmap bmp = new Bitmap(100, 100))
{
  using (Graphics g = Graphics.FromImage(bmp))
  {
    using (Pen p = new Pen(Color.FromArgb(128, Color.Blue), 1))
    {
      using (Brush b = new SolidBrush(Color.FromArgb(128, Color.Blue)))
      {
        g.FillEllipse(b, 0, 0, 99, 99);
        g.FillRegion(b, pictureBox1.Region);

        pictureBox1.BackColor = Color.Transparent;
        pictureBox1.Image = bmp;
      }
    }
  }
}

我总是在代码中使用using,因为它会自动调用对象上的Dispose(),即使using块中出现异常也是如此。我对 SharePoint 项目使用了很多(但这是另一个故事......)。

答案 2 :(得分:3)

C#在超出范围时不会“破坏”或处置它们。

这些类最有可能自动释放他们在其特殊Finalizer方法中保留的非托管资源,这些方法在进入后的不确定时间进行垃圾回收时将被调用超出范围。

但依靠这是依赖于你无法控制的事情,并且可能暂时不会发生。

如果该类实现了IDisposable,那么最佳做法是在某处手动调用Dispose(),或者最好将其包装在using块中。这样你就可以确定:

  

一个。非托管资源是   绝对被释放。

     

B中。释放非托管资源   尽快。

答案 3 :(得分:3)

如果正确使用了一次性模式,Dispose 并非严格必要 - 它将在对象完成时被调用,因此您不会泄漏资源或其他任何内容。 / p>

但是,一旦使用完对象就调用Dispose是很好的方式,因为一次性用品通常直接控制通常有限的本地资源。对象通常不会立即被最终确定/收集,因此一旦您不再使用该对象,这些资源就会被闲置,浪费掉。处置可立即释放这些资源,因此程序的其他部分(或在某些情况下,由其他程序)可以使用它们。

注意,using块会在您完成后自动处理该对象,这就是为什么您很少在Dispose块中看到using的原因。

简短版本:如果对象实现了IDisposable,并且您的代码创建了它(即:如果它不是Pens.Blue之类的系统对象,或者Graphics则传入OnPaint 1}}等等,它应该在你完全完成时处理 - 无论是通过调用Dispose,还是通过调用指定调用Dispose的其他方法(Close是一个常见的,或使用using块。你不来处置它,但你几乎总是应该。

答案 4 :(得分:1)

是的,bmp,g,b和p都是IDisposable,你应该Dispose()所有这些。最好使用using() {}块。

有一些例外情况,当您使用Pen p2 = Pens.Blue;时,您不应该处置p2。它被称为库存项目。对于Brushes.Black等也一样。

对于为什么,所有一次性类都是一样的。 .Net不使用引用计数,因此当引用超出范围时,不存在(不能)立即操作。

将它留给垃圾收集器最终将释放它们,但它(非常)效率低下。我知道一个ASP.NET(!)应用程序由于没有及时处理它而因图形句柄不足而失败。它正在生成图像。

答案 5 :(得分:1)

Dispose用于处理非托管资源。

因此,根据经验,我将IDisposable对象的任何实例化包装在using语句中,因此我不必担心Pen具有哪些非托管资源。