Как MemoryStream, объявленный во внешнем операторе using, все еще доступен после закрытия внутреннего оператора using? - PullRequest
1 голос
/ 07 ноября 2019

Документы Microsoft имеют следующий фрагмент кода на этой странице:

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

Самое внутреннее выражение 'using' предполагает Dispose csEncrypt, который, в свою очередь, предполагает Disposeпоток msEncrypt. Тем не менее, сразу после самой внутренней области применения using msEncrypt все еще жив и используется (вызывается его ToArray ()).

В документе Microsoft четко говорится: «Объект StreamWriter вызывает Dispose () дляпредоставленный объект Stream, когда вызывается StreamWriter.Dispose. "Последнее означает, что csEncrypt также удаляется / закрывается, что, в свою очередь, закрывает msEncrypt (https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,23052627697efb77, Может ли CryptoStream оставить основной поток открытым? ).

Тогдапожалуйста, объясните, как мы все еще можем вызвать 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();
    }
}

Ответы [ 2 ]

1 голос
/ 12 ноября 2019

пожалуйста, объясните, как мы все еще можем вызвать "msEncrypt.ToArray ();"после окончания области действия самого внутреннего оператора using?

Поскольку документация обещает нам, что это так:

This method works when the MemoryStream is closed

(Важно понимать, что в контексте объектов Stream методы Close() и Dispose() фактически являются синонимами. )

В целом,важно помнить, что IDisposable.Dispose() не имеет ничего общего с продолжительностью жизни объекта, реализующего этот интерфейс. Единственное, что он делает, это позволяет вашему коду информировать объект, когда он «сделан с его использованием», чтобы он мог очиститься (как правило, освобождая неуправляемые ресурсы… для любых управляемых объектов, в этом нет необходимости, поскольку сборщик мусора в CLR будетпозаботьтесь об этом).

Любая реализация объекта может делать все, что считает нужным, когда вызывается Dispose(). Хотя типично, что объект стал бы непригодным для использования после вызова Dispose(), это не требуется . И действительно, есть веские причины позволить по крайней мере некоторым методам в MemoryStream, таким как ToArray(), быть пригодными для использования даже после удаления объекта (но учтите, что даже для MemoryStream большинство членов объекта являются непригодными после утилизации… ToArray() - это особый случай).

В любом случае вызов Dispose() never сделает недействительной саму ссылку на объект. Ссылка на объект всегда будет действительной, пока сам объект доступен. Сам объект должен решить, что должно произойти, если какой-либо другой код вызывает одного из его участников после его удаления. В большинстве случаев будет выброшено ObjectDisposedException, но в некоторых конкретных случаях имеет смысл разрешить коду доступ к элементам, которые в первую очередь полезны, только когда код почти завершен с объектом и его основное назначение выполнено. MemoryStream.ToArray() является таким членом.

Смотрите возможные повторяющиеся вопросы: Блок многократного использования, этот код безопасен? Вызов MemoryStream.GetBuffer () завершается успешно даже после MemoryStream.Close ();Почему? Почему вы все еще можете использовать удаленный объект? Разъяснение некоторых вещей об интерфейсе IDisposable. Экземпляр (должен быть) равен нулю после вызова Dispose?

Также смотрите тесно связанный вопрос: CA2202, как решить это дело

1 голос
/ 07 ноября 2019

Метод 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 и добавить обработку исключений, тогда следуйте рекомендованному шаблону здесь.

...