对象引用是如何工作的
假设你有一个随机的对象。该对象是类类型(不是值类型),而不是IDisposable
。基本上这意味着以下内容:
// y is some object
var x = y;
现在,x不会复制y中的所有数据,只是简单地对y的内容进行新的引用。简单。
为确保不会发生内存泄漏,GC会跟踪所有对象并(定期)检查哪些对象可以访问。如果一个对象仍然可以访问,它不会被删除 - 如果不是,它将被删除。
然后有非托管代码
只要你坚持托管代码,一切都很好。当你遇到非托管代码(比如说:GDI +,它是许多System.Drawing内容的本地对象)时,你需要做额外的记录来摆脱代码。毕竟,.NET运行时不太了解非托管数据 - 它只知道有一个指针。因此,GC将清除指针,但不清除数据 - 这会导致内存泄漏。
因此,来自.NET的家伙增加了IDisposable
。通过实现IDisposable,您可以实现额外(非托管)清理,例如释放非托管内存,关闭文件,关闭套接字等。
现在,GC知道终结器,它是作为Disposable模式的一部分实现的:https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx)。但是,您通常不希望等待GC运行来清理非托管资源。因此,当一个对象可以被清理并且具有非托管资源时,通常呼叫Dispose()
是个好主意。
与System.Drawing.Bitmap
一样,它实现了IDisposable
。
在大多数情况下,您可以简单地将IDisposable
包装在using
语句中,该语句将在不错的try/finally子句中为您调用Dispose()。例如:
using (var myBitmap = new Bitmap(...))
{
// use myBitmap
}
// myBitmap including all resources are gone.
什么资源位图
@HansPassant指出,资源位图生成每次你访问一个位图属性时新Bitmap
。这基本上意味着位图被复制并需要处理。
换句话说:
// Free the old bitmap if it exists:
if (this.imageButton.DefaultImage != null)
{
this.imageButton.DefaultImage.Dispose();
this.imageButton.DefaultImage = null;
}
// assign new imageButton.DefaultImage
所以,这解决了内存泄漏,但会给你很多是围绕复制的数据。
如果你不想处理
这里谈到为什么我被从汉斯此言惊讶的部分:)基本上,你每次分配Bitmap
到一个按钮,让你不想一遍又一遍地复制数据 - 这没什么意义。
因此,你可能会得到的想法资源包装成一个“静态”的容器和根本不释放它在所有:
static Bitmap myPic1 = Resource1.Pic1;
static Bitmap myPic2 = Resource1.Pic2;
...
if (this.keyStatus))
{
this.imageButton.DefaultImage = myPic1;
}
else
{
this.imageButton.DefaultImage = myPic2;
}
这工作,但会给你,如果你在某个问题点决定也生成图像。举例说,我们改变这样的代码:
if (this.keyStatus))
{
this.imageButton.DefaultImage = myPic1; // #1 don't dispose
}
else
{
Bitmap myPic3 = CreateFancyBitmap(); // #2 do dispose
this.imageButton.DefaultImage = myPic3;
}
现在,这里的问题是与组合。 myPic1
是一个静态对象,不应该丢弃。另一方面,myPic3
不是,应该处置。如果你打电话Dispose()
,你会在#1处得到一个令人讨厌的异常,因为数据不再存在。没有适当的方法来区分这两者。
资源设计人员在生成正确的代码方面做得不好。 'Pic1'和'Pic2'属性应该是方法。每使用一次该属性它们都会返回一个* new *位图,该位图应该在使用后丢弃。所以在这个非常可怕的borken代码片段中,如果不是null,你必须处理imageButton.DefaultImage。通常,没有100%的保证,这个图像不是来自其他地方,因此可能在其他地方使用。很难评论糟糕的代码。 –
@HansPassant什么?好吧,谢谢你的提高,改变一切。我会改变我的答案; Zorakh似乎并没有明白你的意思。 – atlaste
嗨,谢谢你的回答,虽然我不太清楚你的意思是Pic1和Pic2是返回一个新的位图实例的方法。我特别将它们作为资源包含在我的项目中,以便每次都使用相同(且唯一)的副本,而无需在途中处理实例。如果我的答案是正确的,这里就是这种情况,所以在这里我不必担心这一点(实际上我没有观察到内存问题)。 或者在我上面的示例中是这样的,但是最好是像你说的那样去做?在那种情况下:你能告诉我优点吗? Thx! – Zorakh