Язык программирования C#9 и платформа .NET5
{....try{...} // end .tryfinally{IL_0018: callvirt instance void[System.Runtime]System.IDisposable::Dispose()...} // end handlerIL_001f: ret} // end of method Program::UsingDeclarationПо сути, это новое средство является "магией" компилятора, позволяющей сэкономить несколько нажатий клавиш. При его использовании соблюдайте осторожность, т.к. новый синтаксис не настолько ясен, как предыдущий.
Создание финализируемых и освобождаемых типов
К настоящему моменту вы видели два разных подхода к конструированию класса, который очищает внутренние неуправляемые ресурсы. С одной стороны, можно применять финализатор. Использование такого подхода дает уверенность в том, что объект будет очищать себя сам во время сборки мусора (когда бы она ни произошла) без вмешательства со стороны пользователя. С другой стороны, можно реализовать интерфейс
и предоставить пользователю объекта способ очистки объекта по окончании работы с ним. Тем не менее, если пользователь объекта забудет вызвать методIDisposable, то неуправляемые ресурсы могут оставаться в памяти неопределенно долго.Dispose()Нетрудно догадаться, что в одном определении класса можно смешивать оба подхода, извлекая лучшее из обеих моделей. Если пользователь объекта не забыл вызвать метод
, тогда можно проинформировать сборщик мусора о пропуске процесса финализации, вызвав методDispose(). Если же пользователь объекта забыл вызватьGC.SuppressFinalize(), то объект со временем будет финализирован и получит шанс освободить внутренние ресурсы. Преимущество здесь в том, что внутренние неуправляемые ресурсы будут тем или иным способом освобождены.Dispose()Ниже представлена очередная версия класса
, который теперь является финализируемым и освобождаемым; она определена в проекте консольного приложения C# по имениMyResourceWrapper:FinalizableDisposableClassusing System;namespace FinalizableDisposableClass{// Усовершенствованная оболочка для ресурсов.public class MyResourceWrapper : IDisposable{// Сборщик мусора будет вызывать этот метод, если// пользователь объекта забыл вызвать Dispose().~MyResourceWrapper(){// Очистить любые внутренние неуправляемые ресурсы.// **Не** вызывать Dispose() на управляемых объектах.}// Пользователь объекта будет вызывать этот метод// для как можно более скорой очистки ресурсов.public void Dispose(){// Очистить неуправляемые ресурсы.// Вызвать Dispose() для других освобождаемых объектов,// содержащихся внутри.// Если пользователь вызвал Dispose(), то финализация// не нужна, поэтому подавить ее.GC.SuppressFinalize(this);}}}Обратите внимание, что метод
был модифицирован для вызова методаDispose(), который информирует исполняющую среду о том, что вызывать деструктор при обработке данного объекта сборщиком мусора больше не обязательно, т.к. неуправляемые ресурсы уже освобождены посредством логикиGC.SuppressFinalize().Dispose()Формализованный шаблон освобождения
Текущая реализация класса
работает довольно хорошо, но осталось еще несколько небольших недостатков. Во-первых, методыMyResourceWrapperиFinalize()должны освобождать те же самые неуправляемые ресурсы. Это может привести к появлению дублированного кода, что существенно усложнит сопровождение. В идеале следовало бы определить закрытый вспомогательный метод и вызывать его внутри указанных методов.Dispose()Во-вторых, желательно удостовериться в том, что метод
не пытается освободить любые управляемые объекты, когда такие действия должен делать методFinalize(). В-третьих, имеет смысл также позаботиться о том, чтобы пользователь объекта мог безопасно вызывать методDispose()много раз без возникновения ошибки. В настоящий момент защита подобного рода в методеDispose()отсутствует.Dispose()Для решения таких проектных задач в Microsoft определили формальный шаблон освобождения, который соблюдает баланс между надежностью, удобством сопровождения и производительностью. Вот окончательная версия класса
, в которой применяется официальный шаблон:MyResourceWrapperclass MyResourceWrapper : IDisposable{// Используется для выяснения, вызывался ли метод Dispose().private bool disposed = false;public void Dispose(){// Вызвать вспомогательный метод.// Указание true означает, что очистку// запустил пользователь объекта.CleanUp(true);// Подавить финализацию.GC.SuppressFinalize(this);}