C# Dispose可以正常工作,但不是在终止时

C# Dispose可以正常工作,但不是在终止时,c#,gdi+,C#,Gdi+,我有一个类实现了IDisposable,因为它使用来自GDI+的图像资源(Bitmapclass)。我用它包装所有的噱头锁位/解锁位。无论是在调用Dispose()还是使用语句调用时,它都可以正常工作 但是,如果我让程序终止而不进行处理,我会得到一个System.AccessViolationException。直觉上,我认为GC会像我一样调用Dispose,对象会优雅地释放资源,但事实并非如此。为什么? 以下是IDisposable代码: private bool _disposing =

我有一个类实现了
IDisposable
,因为它使用来自GDI+的图像资源(
Bitmap
class)。我用它包装所有的噱头
锁位
/
解锁位
。无论是在调用
Dispose()
还是使用
语句调用
时,它都可以正常工作

但是,如果我让程序终止而不进行处理,我会得到一个
System.AccessViolationException
。直觉上,我认为GC会像我一样调用
Dispose
,对象会优雅地释放资源,但事实并非如此。为什么?


以下是
IDisposable
代码:

private bool _disposing = false;

~QuickBitmap() {
    Dispose(false);
}

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

private void Dispose(bool safeDispose) {
    if (_disposing)
        return;

    SaveBits(); // private wrapper to UnlockBits
    bytes = null; // byte[] of the image
    bmpData = null; // BitmapData object

    if (safeDispose && bm != null) {
        bm.Dispose(); // Bitmap object
        bm = null;
    }

    _disposing = true;
}
以下是它正常工作的时间:

using (var qbm = new QuickBitmap("myfile.jpg"))
{
    // use qbm.GetPixel/qbm.SetPixel at will
}
当它不起作用时:

public static void Main (string[] args) {
   // this is just an example, simply constructing the object and doing nothing will throw the exception
   var qbm = new QuickBitmap(args[0]);
   qbm.SetPixel(0, 0, Color.Black);
   qbm.Save();
}

完全例外是(没有内部例外):



即使在不同的机器上,复制率也是100%。我想我们应该使用
Dispose()
,不使用它是不好的。我只是想知道:为什么会这样?为什么内存被“保护”?我违反了哪种访问?

IDisposable的全部目的是优雅地清理非托管资源。非托管资源的字面定义是GC无法自行清理的资源。不出所料,如果你不这样做,它将无法自行清理。可以完全通过GC清理的对象不需要是一次性的,这不需要手动处理。如果不需要手动清理对象,那么首先就不需要实现
IDisposable

问题的出现是因为在实现中包含了不必要的终结器。从终结器执行的代码通常无法安全地访问托管对象。调用
SaveBits
可能会导致使用托管对象违反此规则,尽管您没有包含该方法的代码


最好的解决方案是从
QuickBitmap
中删除终结器,因为
QuickBitmap
类并不直接拥有非托管资源。

这根本不能回答我的问题。我很清楚IDisposable
和GC的意思,我要寻找的是手动调用
Dipose
与从析构函数调用
Dipose
之间的区别。@Mephy您询问是否需要手动处置资源以确保其得到处置。是的,就是这样。如果不需要实际使用,
IDisposable
接口,那么首先就不需要它。如果确实需要确保对非托管资源进行清理,则需要手动处置它。仅此而已。私有
位图
是否直接拥有非托管资源?不,
位图
是托管类,而不是非托管资源。未管理的资源可能是由
marshall.AllocHGlobal
创建的
IntPtr
,除了显式调用
Marshal.FreeHGlobal
之外,它不会被释放。只是好奇。。。为什么你认为
LockBits
是“噱头”?@EdS。因为它要求我继续使用
IntPtr
。我使用此类是为了避免在更高级别的类中使用
IntPtr
。您没有遵循规定的Dispose模式。对我来说,这似乎不是一个“噱头”,但Ok有一个模式可以正确创建一个同时具有disposer和finalizer的对象,而此代码实际上不是一个示例。规则一是除非您是垃圾收集器方面的专家,否则不要编写终结器。一旦您成为GC专家,您将首先知道是否需要实现终结器,其次知道如何安全地实现终结器。
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.