在析构函数中擦除 IBuffers

Erasing IBuffers in a Destructor

我有一个 class 在内部管理一个 IBuffer。数据是敏感的,所以我想让 class 确保缓冲区在销毁之前为 0,以避免将这些位留在内存中。我有一个 Erase() 方法,如下所示:

public static void Erase(this IBuffer value)
{
    using (var writer = new DataWriter(value.AsStream().AsOutputStream()))
    {
        for (int i = 0; i < value.Length; i++)
            writer.WriteByte(0);

        var storeTask = Task.Run(async () => await writer.StoreAsync());
        storeTask.Wait();
    }
}

首先,我意识到我在这里使用 Task.Run 来调用非 CPU 绑定的异步方法是值得怀疑的,但我还没有找到同步等效方法。欢迎其他选择。

我遇到的问题是在调试模式下,大多数时候在发布模式下,它 运行 非常完美。但是,偶尔在发布模式下,当我的对象上的 Finalize() 为 运行 时,我会遇到异常:

An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in System.Runtime.WindowsRuntime.dll

Additional information: Excep_InvalidComObject_NoRCW_Wrapper. For more information, visit http://go.microsoft.com/fwlink/?LinkId=623485

URL其实只讲了.Net Native中异常的优化,不是这个具体的异常类型。

我推测原因可能与 IBuffer 在我的 Erase 方法有机会完成之前被销毁有关。

我怎样才能正确地实现我想要的行为?

C# 终结器与 C++ 析构函数不同,尽管有时使用术语 "destructor" 来描述它们。 C# 终结器根本不能保证 运行。如果终结器执行 运行,它 运行 相对于其他终结器的顺序未定义,因此您的终结器不能依赖于访问本身具有终结器的对象(例如 COM 对象包装器) .

所以是的,如果您依赖终结器并且您的终结器试图使用一个本身可能有终结器的对象,您可能会发现当您的终结器 运行s 时,它是尝试使用可能已经清理干净了。

确实,如果您实现 Dispose(),一种策略是同时实现终结器(另一种是使用 SafeHandle 子类来包装非托管资源)。但这只是一个后盾,因为终结器不能保证 运行,它不是 100% 可靠的。实施终结器的指南并不是因为这是一种 100% 可靠的清理方法,而是因为如果您正在处理忘记调用 Dispose().


所以,是的……在 C# 中,正确的策略是实现 IDisposable 并要求希望安全清理内存的客户端确保他们遵守规则并在完成对象后调用 Dispose() .


顺便说一下,就你的 Task.Run() 而言……

您应该异步使用异步方法。例如。不要实现 Erase(),而是实现 EraseAsync() 并在里面使用 await

但如果您真的坚持要等待它们,则无需将调用包装在您使用 Task.Run() 执行的匿名 async 方法中。那是大材小用。只需等待 StoreAsync() 返回的任务对象。你可以这样做,例如通过调用它的 GetResults() 方法(应该阻塞直到结果实际可用),或者您可以使用 AsTask() extension method 直接转换为 Task<T> 对象,当然然后等待该任务对象。