【问题标题】:How do I utilize the "using" statement correctly for my code如何为我的代码正确使用“使用”语句
【发布时间】:2011-09-18 16:08:32
【问题描述】:

这是我的代码:

Bitmap bmp = ImageManipulator.GetMyImageModified(bmp);
Bitmap tempBMP = ImageManipulator.cropImage(bmp, rect);
tempBMP = ImageManipulator.CopyToBpp(tempBMP, 1);

string bmpFilename = String.Format("File{0}.png", indexNum);
tempBMP.Save(bmpFilename, ImageFormat.Png); 

现在我已经看到,对于 IDisposable 对象,使用 using 语句在不再需要这些对象时立即处置它们是一种最佳做法。 我想遵循这种做法,所以我需要一些帮助来重写上面的代码:

using (Bitmap bmp = ImageManipulator.GetMyImageModified(bmp){

   Bitmap tempBMP = ImageManipulator.cropImage(bmp, rect); // bmp should be disposed after this line
   tempBMP = ImageManipulator.CopyToBpp(tempBMP, 1);

   string bmpFilename = String.Format("File{0}.png", indexNum);
   tempBMP.Save(bmpFilename, ImageFormat.Png); 
} // bmp is disposed here

这是我的第一次尝试,但它并不完美,因为 bmp 位图一旦不再需要就不会被释放,尽管在这个特定示例中不应该强制如此快速地释放它。

tempBitmap 更成问题,因为我无法将引用重新分配给 using 语句中应该包含的新对象:

Bitmap tempBMP = ImageManipulator.cropImage(bmp, rect);

确实 tempBMP 引用在用 using 关键字包围上述行后变为 readonly

tempBMP 也被保存到一个文件中,并且保存操作应该是异步的,然后我不知道处置的影响:

tempBMP.Save(bmpFilename, ImageFormat.Png); 

被调用。

如果你能帮我写出更好的代码,我会全力以赴。

【问题讨论】:

  • Save() 不是异步的。
  • tempBMP 中有两个不同的位图,您应该将它们都处理掉。所以无论如何你都需要把它分成两个变量。
  • @Slaks 感谢您解决问题。
  • 一般来说,至少对于像位图这样的可替代资源,人们不应该担心在第一时间调用 IDisposable 上的 Dispose。重要的是 Dispose 在可预见的时间范围内被调用。通常,使用块嵌套并让较晚创建的对象在较早创建的对象之前被释放,比以更任意的顺序创建和销毁对象要好。

标签: c# object idisposable using


【解决方案1】:
using (var sourceBmp = ...)
using (var modifiedBmp = ImageManipulator.GetMyImageModified(sourceBmp))
using (var croppedBmp = ImageManipulator.cropImage(modifiedBmp, rect))
using (var finalBmp = ImageManipulator.CopyToBpp(croppedBmp, 1))
{
    string bmpFilename = String.Format("File{0}.png", indexNum);
    finalBmp.Save(bmpFilename, ImageFormat.Png); 
}

【讨论】:

  • 是的,这看起来是正确的,并且拆分变量是要走的路。在这种情况下,“var”的用法是方便、需要还是更好的做法?
  • @Relok,这更多是个人喜好。 var 关键字的用法已经在 StackOverflow 上讨论过,我不想影响你的决定。我个人一直使用它,因为我很懒。
  • 无论如何,对于每个位图,处理都发生在最后一个使用 var finalBmp 代码块之后,对吧?所以它基本上就像我在我的问题中写的那样,不同之处在于你的代码处理所有位图而不是只处理“bmp”,对吧?
  • 与您的回答相关的小插件问题:如果在我的原始代码中我有一个 if 语句来检查 sourceBmp 是否为空,我该如何编写 using 块?我可以绕过这个在代码 sn-p 的每个方法中检查 null,但我仍然想知道如果我不选择这种方式,我应该如何更改我的代码。
【解决方案2】:

using 关键字只是为了方便。显式调用 Dispose() 仍然很有可能。在 finally 块中这样做。随意:

        Bitmap bmp = ImageManipulator.GetMyImageModified(bmp);
        var t = new Thread(() => {
            try {
                using (var croppedBmp = ImageManipulator.cropImage(bmp, rect))
                using (var copiedBmp = ImageManipulator.CopyToBpp(tempBMP, 1)) {
                    string bmpFilename = String.Format("File{0}.png", indexNum);
                    copiedBmp.Save(bmpFilename, ImageFormat.Png);
                }
            }
            catch (Exception ex) {
                ReportFailure(ex);
            }
            finally {
                bmp.Dispose();
            }
        });
        t.Start();

【讨论】:

  • 关于您的示例的问题,该示例使用新线程的 lamda 表达式。 finally 语句发生在新线程内,从我看到的这种方式我可以将任意数量的参数传递给它,在你的示例中为 bmp 位图。但是如果 bmp 超出了主线程中调用的方法的范围,而新线程还没有完成,会发生什么?
  • 什么都没有。 bmp 变量值被 lambda 捕获。 indexNum 变量值也是如此。
  • 如果在创建或启动新线程时抛出异常,bmp 将被泄露。此外,正如 Relok 的回答中所指出的,看起来无论最初在 bmp 中的任何位图都可能被泄露。
  • 如果创建线程抛出异常,那么游戏就结束了。当剩下的系统资源如此之少时,尝试继续是没有意义的。
  • @supercat 您能否更好地解释您的最后陈述以及为什么“无论如何”都可能发生泄漏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-13
  • 2020-09-04
  • 2022-06-13
  • 1970-01-01
  • 2020-09-01
  • 2015-09-15
  • 1970-01-01
相关资源
最近更新 更多