1

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

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

最内部的“使用”语句假设 Dispose csEncrypt,而后者又假设 Dispose msEncrypt 流。但是,在最内部的 using 语句范围之后,msEncrypt 仍然存在并被使用(调用它的 ToArray())。

微软文档明确指出:“当 StreamWriter.Dispose 被调用时,StreamWriter 对象在提供的 Stream 对象上调用 Dispose()。”。后者意味着 csEncrypt 也被处置/关闭,这反过来又关闭了 msEncrypt ( https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,23052627697efb77CryptoStream 可以离开基础流打开?)。

然后请解释我们如何仍然可以调用“msEncrypt.ToArray();” 在最里面的 using 语句的范围结束之后?

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();
    }
}
4

2 回答 2

1

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

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

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语句并添加异常处理,请遵循此处推荐的模式。

于 2019-11-07T19:19:15.360 回答
1

请解释我们如何仍然可以调用“msEncrypt.ToArray();” 在最里面的 using 语句的范围结束之后?

因为文档向我们保证是这样的:

此方法在 MemoryStream 关​​闭时有效

(重要的是要理解在Stream对象的上下文中,Close()方法Dispose()实际上是同义词。

更一般地说,重要的是要记住,这与实现该接口的对象的生命周期IDisposable.Dispose()无关。它所做的唯一一件事就是让您的代码在“完成使用它”时通知对象,以允许它清理(通常,释放非托管资源……对于任何托管对象,没有必要,因为 CLR 的垃圾收集器将照顾那些)。

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

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

查看可能的重复问题:
多个 using 块,此代码安全吗?
即使在 MemoryStream.Close() 之后,对 MemoryStream.GetBuffer() 的调用也会成功;为什么?
为什么您仍然可以使用已处置的对象?
澄清一些关于 IDisposable 接口的事情。调用 Dispose 后实例(必须)是否等于 null?

另请参阅密切相关的问题:
CA2202,如何解决这种情况

于 2019-11-12T18:15:30.640 回答