Реальный смысл IDisposable
является то, что объект знает что-то, где-то, который был введен в состояние, которое должно быть вычищен, и он имеет информацию и стимулы, необходимые для выполнения такой очистки. Хотя наиболее распространенными «состояниями», связанными с IDisposable
, являются такие вещи, как открытые файлы, неуправляемые графические объекты и т. Д., Это только примеры использования, а не определение «правильного» использования.
Самая большая проблема, необходимо учитывать при использовании IDisposable
и using
для области видимости поведения является то, что нет никакого способа для метода Dispose
различать сценарии, где исключение из using
блока из тех, где она выходит нормально. Это печально, так как существует много ситуаций, когда было бы полезно иметь видимое поведение, которое гарантировало бы иметь один из двух путей выхода в зависимости от того, был ли выход нормальным или ненормальным.
Рассмотрим, например, объект блокировки считывающего устройства с методом, который возвращает «токен» IDisposable
при получении блокировки. Было бы хорошо, чтобы сказать:
using (writeToken = myLock.AcquireForWrite())
{
... Code to execute while holding write lock
}
Если бы нужно было вручную кодировать приобретение и освобождение замка без Try/уловом или попытаться /, наконец, заблокировать, исключение брошено в то время как замок был проведен бы вызвать любой код который ждал на замке, чтобы ждать вечно. Это плохо. Использование блока using
, как показано выше, приведет к тому, что блокировка будет освобождена при выходе из блока, как обычно, так и через исключение. К сожалению, это может быть также будет плохой штукой.
Если неожиданное исключение выбрасывается при блокировке записи, самым безопасным поведением будет . Заблокируйте блокировку, чтобы любая настоящая или будущая попытка захвата блокировки немедленно выдала бы исключение. Если программа не может с пользой действовать без использования заблокированного ресурса, такое поведение приведет к ее быстрому завершению. Если это может продолжаться, например. переключаясь на какой-то альтернативный ресурс, недействительность ресурса позволит ему справиться с этим намного эффективнее, чем оставление бесполезной блокировки. К сожалению, я не знаю ничего хорошего, чтобы это сделать. Можно было бы сделать что-то вроде:
using (writeToken = myLock.AcquireForWrite())
{
... Code to execute while holding write lock
writeToken.SignalSuccess();
}
и есть метод Dispose
недействительным маркер, если он вызывается до успеха было сигналом, но случайная неудача сигнализировать успех может привести ресурс стать недействительным без предоставления указания на где и почему это произошло. Если метод Dispose
выдает исключение, если код выходит из блока using
, как правило, без вызова SignalSuccess
может быть хорошим, за исключением того, что исключение исключения при выходе из него из-за какого-либо другого исключения уничтожит всю информацию об этом другом исключении, и нет способа сообщить Dispose
который применяется.
Учитывая эти соображения, я думаю, что лучше всего, вероятно, использовать что-то вроде:
using (lockToken = myLock.CreateToken())
{
lockToken.AcquireWrite(Describe how object may be invalid if this code fails");
... Code to execute while holding write lock
lockToken.ReleaseWrite();
}
Если код выходит без вызова ReleaseWrite
, других потоков, которые пытаются получить блокировку будет получать исключения, которые включают в себя указанное сообщение , Неправильная установка вручную пары AcquireWrite
и ReleaseWrite
оставит заблокированный объект непригодным для использования, но не оставит другого кода, ожидая его использования. Обратите внимание, что неуравновешенный AcquireRead
не должен будет аннулировать объект блокировки, поскольку код внутри чтения никогда не помещает объект в недопустимое состояние.
Пожалуйста, d o не использовать Dispose для бизнес-логики. Это механизм для очистки неуправляемых ресурсов; используйте его для своей цели. –
@EricLippert: будет ли 'TransactionScope' исключением, которое доказывает правило? – user7116
@ user7116: Я отмечаю, что многие люди не понимают, что в этом высказывании «доказывает» означает «испытуемые». Отсюда выражение «доказательство» для диапазона испытаний оружия. Да, это исключение, которое * доказывает * - субъекты для тестирования - правило.Я считаю, что результатом этого теста является то, что использование несколько подозрительно; почему вызов «Завершить» не выполняет коммит, а «Dispose» просто закрывает соединение? Я также отмечаю, что документация противоречит самому вопросу о том, завершает ли 'Complete' или' Dispose' коммит. –