C # 2008
Я работал над этим некоторое время, и я все еще не уверен в использовании методов finalize и dispose в коде. Мои вопросы ниже:
Я знаю, что нам нужен только финализатор при утилизации неуправляемых ресурсов. Однако, если есть управляемые ресурсы, которые выполняют вызовы к неуправляемым ресурсам, нужно ли все же реализовать финализатор?
Однако, если я разрабатываю класс, который не использует какой-либо неуправляемый ресурс - прямо или косвенно, должен ли я реализовать так,
IDisposable
чтобы клиенты этого класса могли использовать «оператор using»?Реально ли реализовать IDisposable просто для того, чтобы клиенты вашего класса могли использовать оператор using?
using(myClass objClass = new myClass()) { // Do stuff here }
Я разработал этот простой код ниже, чтобы продемонстрировать использование Finalize / dispose:
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
Вопрос по исходному коду:
Здесь я не добавил финализатор, и обычно финализатор вызывается GC, а финализатор вызывает Dispose. Поскольку у меня нет финализатора, когда я вызываю метод Dispose? Это клиент класса должен вызывать его?
Итак, мой класс в примере называется NoGateway, и клиент может использовать и распоряжаться этим классом так:
using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here }
Будет ли метод Dispose автоматически вызываться, когда выполнение достигает конца блока using, или клиент должен вручную вызывать метод dispose? т.е.
NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
Я использую
WebClient
класс в своемNoGateway
классе. ПосколькуWebClient
реализуетIDisposable
интерфейс, означает ли это, чтоWebClient
косвенно использует неуправляемые ресурсы? Есть ли жесткое и быстрое правило следовать этому? Как я узнаю, что класс использует неуправляемые ресурсы?
источник
Ответы:
Рекомендуемый шаблон IDisposable находится здесь . При программировании класса, который использует IDisposable, обычно вы должны использовать два шаблона:
При реализации запечатанного класса, который не использует неуправляемые ресурсы, вы просто реализуете метод Dispose, как в обычных реализациях интерфейса:
При реализации открытого класса сделайте это так:
Обратите внимание, что я не объявил финализатор в
B
; вы должны реализовывать финализатор только в том случае, если у вас есть реальные неуправляемые ресурсы, которыми можно распоряжаться. CLR имеет дело с финализуемыми объектами иначе, чем не финализируемые объекты, даже еслиSuppressFinalize
вызывается.Таким образом, вы не должны объявлять финализатор, если в этом нет необходимости, но вы предоставляете наследникам вашего класса ловушку для вызова вашего
Dispose
и реализации финализатора самостоятельно, если они напрямую используют неуправляемые ресурсы:Если вы не используете неуправляемые ресурсы напрямую (
SafeHandle
а друзья не учитываются, поскольку они объявляют свои собственные финализаторы), то не реализуйте финализатор, поскольку сборщик мусора обрабатывает финализируемые классы по-разному, даже если позже вы подавите финализатор. Также обратите внимание, что, хотя уB
него нет финализатора, он все равно вызывает,SuppressFinalize
чтобы правильно иметь дело с любыми подклассами, которые реализуют финализатор.Когда класс реализует интерфейс IDisposable, это означает, что где-то есть неуправляемые ресурсы, от которых нужно избавиться, когда вы закончите использовать класс. Фактические ресурсы заключены в классы; вам не нужно явно удалять их. Простое обращение
Dispose()
к классу или его обертываниеusing(...) {}
обеспечит удаление любых неуправляемых ресурсов по мере необходимости.источник
IDisposable
, скорее всего, это будет зависать некоторое время. Вы сохраняете CLR, пытаясь скопировать его из Gen0 -> Gen1 -> Gen2Официальную схему реализации
IDisposable
сложно понять. Я считаю , что это один лучше :Еще лучшее решение - иметь правило, согласно которому вам всегда нужно создавать класс-оболочку для любого неуправляемого ресурса, с которым вам нужно работать:
С
SafeHandle
и его производными, эти классы должны быть очень редкими .Результат для одноразовых классов, которые не имеют непосредственного отношения к неуправляемым ресурсам, даже при наличии наследования, является мощным: им больше не нужно заботиться о неуправляемых ресурсах . Их будет просто реализовать и понять:
источник
disposed
флаг и проверить соответственно.disposed
флаг, проверить его перед утилизацией и установить после утилизации. Ищите здесь идею. Вы также должны проверить флаг перед любыми методами класса. Имеет смысл? Это сложно?Обратите внимание, что любая реализация IDisposable должна следовать шаблону ниже (IMHO). Я разработал этот шаблон на основе информации от нескольких превосходных «богов» .NET - Руководства по проектированию .NET Framework (обратите внимание, что MSDN по какой-то причине не следует этому!). Руководство по проектированию .NET Framework было написано Кшиштофом Квалиной (архитектором CLR в то время) и Брэдом Абрамсом (я считаю, что руководителем программы CLR в то время) и Биллом Вагнером ([Эффективный C #] и [Более эффективный C #] (просто возьмите ищите их на Amazon.com:
Обратите внимание, что вы НИКОГДА не должны реализовывать Finalizer, если ваш класс не содержит (не наследует) неуправляемых ресурсов. Как только вы реализуете Finalizer в классе, даже если он никогда не вызывается, он гарантированно будет жить для дополнительной коллекции. Он автоматически помещается в очередь финализации (которая выполняется в одном потоке). Кроме того, одно очень важное замечание ... весь код, выполняемый в Финализаторе (если вам необходимо его реализовать), ДОЛЖЕН быть поточно-ориентированным и исключительным! ПЛОХИЕ вещи произойдут иначе ... (то есть неопределенное поведение и в случае исключения фатальный неисправимый сбой приложения).
Шаблон, который я собрал (и написал фрагмент кода), выглядит следующим образом:
Вот код для реализации IDisposable в производном классе. Обратите внимание, что вам не нужно явно указывать наследование от IDisposable в определении производного класса.
Я разместил эту реализацию в своем блоге по адресу: Как правильно реализовать шаблон утилизации
источник
Interlocked.Exchange
целочисленногоIsDisposed
флага в не виртуальной функции-оболочке было бы безопаснее.Я согласен с pm100 (и должен был четко сказать это в моем предыдущем посте).
Вы никогда не должны реализовывать IDisposable в классе, если вам это не нужно. Чтобы быть очень конкретным, есть примерно 5 раз, когда вам понадобится / нужно реализовать IDisposable:
Ваш класс явно содержит (т.е. не через наследование) любые управляемые ресурсы, которые реализуют IDisposable и должны быть очищены, когда ваш класс больше не используется. Например, если ваш класс содержит экземпляр Stream, DbCommand, DataTable и т. Д.
Ваш класс явно содержит любые управляемые ресурсы, которые реализуют метод Close () - например, IDataReader, IDbConnection и т. Д. Обратите внимание, что некоторые из этих классов реализуют IDisposable с помощью метода Dispose (), а также метода Close ().
Ваш класс явно содержит неуправляемый ресурс - например, COM-объект, указатели (да, вы можете использовать указатели в управляемом C #, но они должны быть объявлены в «небезопасных» блоках и т. Д. В случае неуправляемых ресурсов вы также должны убедиться, что вызовите System.Runtime.InteropServices.Marshal.ReleaseComObject () в RCW. Несмотря на то, что RCW, теоретически, является управляемой оболочкой, все еще продолжается подсчет ссылок под прикрытием.
Если ваш класс подписывается на события, используя сильные ссылки. Вы должны отменить регистрацию / отсоединиться от событий. Всегда проверяйте, чтобы они не были нулевыми, прежде чем пытаться отменить их регистрацию!
Ваш класс содержит любую комбинацию из вышеперечисленного ...
Рекомендуемой альтернативой работе с COM-объектами и использованием Marshal.ReleaseComObject () является использование класса System.Runtime.InteropServices.SafeHandle.
BCL (команда библиотек базовых классов) имеет хороший пост в блоге об этом здесь http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
Одно очень важное замечание: если вы работаете с WCF и очищаете ресурсы, ПОЧТИ ВСЕГДА следует избегать блока «использование». Есть много постов в блоге и некоторые на MSDN о том, почему это плохая идея. Я также написал об этом здесь - не используйте 'using ()' с прокси WCF
источник
Использование лямбды вместо IDisposable.
Я никогда не был в восторге от всей идеи использования / IDisposable. Проблема в том, что он требует от вызывающего абонента:
Мой новый предпочтительный метод состоит в том, чтобы использовать фабричный метод и лямбду
Представьте, что я хочу сделать что-то с SqlConnection (что-то, что должно быть заключено в использование). Классически вы бы сделали
Новый путь
В первом случае вызывающая сторона может просто не использовать синтаксис using. Во втором случае у пользователя нет выбора. Нет метода, который создает объект SqlConnection, вызывающая сторона должна вызывать DoWithConnection.
DoWithConnection выглядит так
MakeConnection
сейчас приватныйисточник
DoForAll(Action<T>) where T:IComparable<T>
, вызывающий указанный делегат для каждой записи. Учитывая два таких объекта, каждый из которых будет возвращать данные в отсортированном порядке, как один из них выведет все элементы, которые существуют в одной коллекции, но не другой? Если реализованы типыIEnumerable<T>
, можно выполнить операцию слияния, но это не сработаетDoForAll
.DoForAll
коллекции без необходимости сначала полностью копировать одну коллекцию в какую-то другую структуру - это использовать два потока, что было бы гораздо сложнее, чем просто использование пары IEnumerable и соблюдение осторожности. выпустить их.никто не ответил на вопрос о том, следует ли вам реализовать IDisposable, даже если он вам не нужен.
Краткий ответ: нет
Длинный ответ:
Это позволит потребителю вашего класса использовать «использование». Вопрос, который я хотел бы задать, - зачем им это делать? Большинство разработчиков не будут использовать «использование», если они не знают, что они должны - и как они знают. Или
Поэтому, реализуя IDisposable, вы говорите разработчикам (по крайней мере, некоторым), что этот класс завершает что-то, что должно быть выпущено. Они будут использовать «использование» - но в других случаях использование невозможно (область действия объекта не локальна); и им придется начать беспокоиться о времени жизни объектов в тех других случаях - я бы беспокоился наверняка. Но это не обязательно
Вы реализуете Idisposable, чтобы они могли использовать использование, но они не будут использовать использование, если вы не скажете им об этом.
Так что не делай этого
источник
Если вы используете другие управляемые объекты, которые используют неуправляемые ресурсы, вы не несете ответственности за их завершение. Ваша обязанность - вызывать Dispose для этих объектов, когда Dispose вызывается для вашего объекта, и он на этом останавливается.
Если ваш класс не использует ограниченные ресурсы, я не понимаю, почему вы заставили бы ваш класс реализовать IDisposable. Вы должны делать это только если вы:
Да, код, который использует ваш код, должен вызывать метод Dispose вашего объекта. И да, код, который использует ваш объект, может использовать,
using
как вы показали.(Снова 2?) Вероятно, что WebClient использует неуправляемые ресурсы или другие управляемые ресурсы, которые реализуют IDisposable. Точная причина, однако, не важна. Важно то, что он реализует IDisposable, и поэтому вам приходится действовать на основе этих знаний, избавляясь от объекта, когда вы закончите с ним, даже если выясняется, что WebClient вообще не использует никаких других ресурсов.
источник
Удалите шаблон:
Пример наследования:
источник
Некоторые аспекты другого ответа немного некорректны по двум причинам:
Первый,
на самом деле эквивалентно:
Это может показаться нелепым, поскольку оператор 'new' никогда не должен возвращать 'null', если у вас нет исключения OutOfMemory. Но рассмотрим следующие случаи: 1. Вы вызываете FactoryClass, который возвращает ресурс IDisposable, или 2. Если у вас есть тип, который может или не может наследоваться от IDisposable, в зависимости от его реализации - помните, что я видел, что шаблон IDisposable реализован неправильно во многих время на многих клиентах, где разработчики просто добавляют метод Dispose (), не наследуя от IDisposable (плохо, плохо, плохо). Вы также можете иметь дело с возвращением ресурса IDisposable из свойства или метода (опять же, плохо, плохо, плохо - не отдавайте свои ресурсы IDisposable)
Если оператор «as» возвращает ноль (или свойство или метод, возвращающий ресурс), а ваш код в блоке «using» защищает от «ноль», ваш код не будет взорван при попытке вызвать Dispose для нулевого объекта из-за встроенная нулевая проверка.
Вторая причина, по которой ваш ответ не точный, заключается в следующем:
Во-первых, финализация (как и сама сборка мусора) является недетерминированной. CLR определяет, когда он вызовет финализатор. т.е. разработчик / код понятия не имеет. Если шаблон IDisposable реализован правильно (как я уже писал выше) и был вызван GC.SuppressFinalize (), не будет вызван финализатор. Это одна из главных причин для правильной реализации шаблона. Поскольку для каждого управляемого процесса существует только 1 поток Finalizer, независимо от количества логических процессоров, вы можете легко снизить производительность, создав резервную копию или даже повесив поток Finalizer, забыв вызвать GC.SuppressFinalize ().
Я разместил правильную реализацию шаблона Dispose в моем блоге: Как правильно реализовать шаблон Dispose
источник
NoGateway = new NoGateway();
иNoGateway != null
?1) WebClient является управляемым типом, поэтому вам не нужен финализатор. Финализатор необходим в том случае, если ваши пользователи не Dispose () вашего класса NoGateway и после этого необходимо очистить собственный тип (который не собирается GC). В этом случае, если пользователь не вызывает Dispose (), содержащийся WebClient будет удален GC сразу после того, как это сделает NoGateway.
2) Косвенно да, но вам не нужно об этом беспокоиться. Ваш код верен в действии, и вы не можете помешать своим пользователям очень легко забыть Dispose ().
источник
Шаблон от MSDN
источник
эквивалентно
Финализатор вызывается при уничтожении вашего объекта GC. Это может быть в совершенно другое время, чем когда вы покидаете свой метод. Dispose of IDisposable вызывается сразу после выхода из блока using. Следовательно, шаблон обычно используется для освобождения ресурсов сразу после того, как они вам больше не нужны.
источник
Из того, что я знаю, настоятельно рекомендуется НЕ использовать Финализатор / Деструктор:
Главным образом, это происходит из-за незнания, когда или ЕСЛИ это будет вызвано. Метод утилизации намного лучше, особенно если вы используете или утилизируете напрямую.
использование это хорошо. используй это :)
источник