虽然虚拟方法分派使事情稍微复杂一些,但实例方法的行为很大程度上类似于this
作为第一个参数传递的静态方法。因此,在 classFoo
中,具有字段int Bar
的方法
void SetBar(int newBar) { Bar = newBar; }
内部等效于:
static void SetBar(Foo This, int newBar) { This.Bar = newBar; }
如果Foo
是一个名为的字段MyBitmap
,它持有IntPtr
非托管位图的句柄,并且具有如下方法:
// Should call GC.SuppressFinalize, but doesn't.
static Byte[] ExportBitmapDataAndDispose(Foo This)
{
IntPtr myBits = This.MyBitMap;
if (myBits == 0) throw new ObjectDisposedException(...);
int destSize = ExternalBitmapHandler.GetSize(myBits);
var result = new Byte[destSize];
ExternalBitmapHandler.CopyBits(myBits, ref result[0], destSize);
ExternalBitmapHandler.ReleaseBits(myBits);
myBits = 0;
}
编译器会看到在从中读取This
字段后从未使用过。MyBitMap
代码仍然需要由 标识的外部资源myBits
,但垃圾收集器对此一无所知。从它的角度来看,如果 引用的对象This
没有其他实时引用,则代码不应该关心这些对象是在那个时候不再存在还是被保留更长时间。事实上,它的假设是正确的,因为运行代码确实不会关心这些对象何时停止存在。不幸的是,在终结器存在的情况下,对象并不仅仅不复存在。相反,如果垃圾收集器注意到 Finalization Queue 拥有对 的唯一实时引用This
,它可能会运行This.Finalize()
,这可能反过来通知ExternalBitmapHandler
myBits
不再需要由 标识的位图。
请注意,问题实际上不在于 的垃圾收集,This
而在于. This
终结不是垃圾收集,而是当 GC 发现如果不是已注册的终结器有资格立即销毁的对象时触发的操作。另一种看待它的方式是说当不再需要ExternalBitmapHandler
由 标识的位图时应该通知它This.MyBitMap
,但垃圾收集器唯一可以告诉的是何时This
不再需要。一旦代码读This.MyBitMap
入myBits
,它就不再需要This
并且非常高兴如果This
不复存在,只要它没有破坏 `myBits. 代码的替代版本可能是:
static Byte[] ExportBitmapDataAndDispose(Foo This)
{
IntPtr myBits = System.Threading.Interlocked.Exchange(ref This.MyBitMap, 0);
GC.SuppressFinalize(This);
if (myBits == 0) throw new ObjectDisposedException(...);
int destSize = ExternalBitmapHandler.GetSize(myBits);
try
{
var result = new Byte[destSize]; // Could throw OutOfMemoryException
ExternalBitmapHandler.CopyBits(myBits, ref result[0], destSize);
}
finally
{
ExternalBitmapHandler.ReleaseBits(myBits);
}
}
请注意,在这种情况下,没有KeepAlive
,也没有将SuppressFinalize
代码放在最后,它会推迟任何完成的可能性,直到一切都完成。一旦Interlocked.Exchange
发生了,系统破坏就没有什么问题了This
,因为它不再需要任何东西;系统需要让它保持足够长的时间才能执行SuppressFinalize
,但之后它可能会消失并且没有人会注意到。