内部using语句关闭后,外部using语句中声明的MemoryStream如何仍然可用?

How the MemoryStream declared in outer using statement is still available after inner using statement closes?

Microsoft 文档在此页面上有以下代码片段:

https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptostream?view=netframework-4.7

最内部的 'using' 语句假定要处置 csEncrypt,而这又假定要处置 msEncrypt 流。但是,就在最内部的 using 语句范围之后,msEncrypt 仍然存在并被使用(调用它的 ToArray())。

微软文档明确指出:"The StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called."。后者意味着 csEncrypt 也是 disposed/closed,它又关闭了 msEncrypt (https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,23052627697efb77, Can a CryptoStream leave the base Stream open?)。

那请解释一下我们如何在最里面的using语句作用域结束后仍然调用"msEncrypt.ToArray();"?

using (MemoryStream msEncrypt = new MemoryStream())
{
    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
    {
        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
        {
            //Write all data to the stream.
            swEncrypt.Write(plainText);
        }

        encrypted = msEncrypt.ToArray();
    }
}

这些对象中的Dispose方法释放了非托管资源,它不会立即销毁整个对象。此外,当不再使用该对象时,垃圾收集器会自动释放分配给该对象的内存,但由于您仍然在最里面的using块之外使用它,它赢了尚未被垃圾收集。这就是为什么您仍然可以调用 ToArray 方法的原因,因为它不使用任何底层非托管资源 并且 对象尚未被垃圾回收。此外,无法预测对象不再使用后何时会发生垃圾回收。

如果多次调用对象的 Dispose 方法,对象必须忽略第一次调用之后的所有调用。如果多次调用对象的 Dispose 方法,则该对象不得抛出异常。 大多数人甚至建议抑制此警告。更多信息 here。 您的代码的重构版本(没有异常处理)可能是这样的:

MemoryStream msEncrypt = new MemoryStream();

CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

StreamWriter swEncrypt = new StreamWriter(csEncrypt)

//Write all data to the stream.
swEncrypt.Write(plainText);
encrypted = msEncrypt.ToArray();

//Do more work if needed.
//if you need to dispose of them manually and the current scope hasn't ended then just do:
swEncrypt.Dispose();

或者如果您想维护 using 语句并添加异常处理,请遵循推荐的模式 here

please explain how we can still call the "msEncrypt.ToArray();" after the end of scope of the innermost using statement?

因为 the documentation 向我们保证是这样的:

(重要的是要了解在 Stream 个对象的上下文中,the Close() and Dispose() methods are effectively synonymous.

更一般地说,重要的是要记住 IDisposable.Dispose() 与实现该接口的对象的 lifetime 完全无关。它做的唯一一件事是允许您的代码在对象 "done using it" 时通知对象,以允许它清理(通常,释放非托管资源......对于任何托管对象,没有必要,因为 CLR 的垃圾收集器将照顾那些)。

任何对象实现都可以在调用 Dispose() 时做任何它认为合适的事情。虽然在调用 Dispose() 后对象变得不可用是典型的,但这不是 必需的 。事实上,有充分的理由允许 MemoryStream 中的至少一些方法,如 ToArray(),在对象被释放后仍然可用(但请注意,即使对于 MemoryStream,大多数对象的成员在处置后无法使用……ToArray()是一个特例)。

在任何情况下,调用 Dispose() never 都会使对象引用本身无效。只要对象本身是可访问的,对象引用将始终保持有效。如果任何其他代码在释放后调用其成员之一,则由对象本身决定应该发生什么。大多数时候,ObjectDisposedException 将被抛出,但在某些特定情况下,允许代码访问主要有用的成员是有意义的,只有当代码接近完成对象并且其主要目的已达到时. MemoryStream.ToArray()就是这样的会员。

查看可能重复的问题:

Call to MemoryStream.GetBuffer() succeeds even after MemoryStream.Close(); Why?

Clarify some things about IDisposable interface. Is instance (must be) equals null after calling Dispose?

另见密切相关的问题:
CA2202, how to solve this case