Является ли вызов Close для класса в предложении using полезным, вредным или спорным?
При рефакторинге некоторого кода я добавил выражение "using", например, так:
using (SerialPort serialPort = new SerialPort())
{
serialPort.BaudRate = 19200;
serialPort.Handshake = Handshake.XOnXOff;
serialPort.Open();
serialPort.Write(cmd);
serialPort.Close();
}
... но теперь задаюсь вопросом, могу ли я покончить со звонком в Close. Я полагаю, что так, но это просто изящность (стиль) или истинная необходимость?
4 ответа
Это действительно зависит от конкретного класса, реализующего IDisposable. Для плохо написанного класса, который реализует IDisposable NOT, вполне возможно правильно высвобождать ресурсы и закрывать соединения. В конкретном случае класса SerialPort в документации говорится, что Close() вызывает Dispose(). Я думаю, что в этом случае вам следует поместить его в блок using и не вызывать Close() вручную.
Это действительно зависит от класса, и если автор реализовал его в соответствии с рекомендациями - реализовал IDisposable
правильно, и у Закрыть ничего не делать, кроме как позвонить Dispose()
, В таком случае Close()
Вызов избыточен и может быть удален при переносе класса в using
блок.
Это вообще говоря - в данном конкретном случае с SerialPort
они так и сделали, поэтому Close()
вызов избыточен и может быть удален.
Согласно Рихтеру:
Типы, которые предлагают возможность детерминированного удаления или закрытия, реализуют шаблон удаления. Шаблон dispose определяет соглашения, которых должен придерживаться разработчик при определении типа, который хочет предложить явную очистку пользователю типа.
Тот факт, что SerialPort определяет открытие и закрытие, подразумевает, что его можно открывать и закрывать несколько раз в течение срока его службы, что взаимно исключает его удаление (например, никогда не используется снова)
Но вернемся к вашему первоначальному вопросу, да - в этом случае это избыточно, декомпиляция объекта SerialPort показывает, что утилита закрывает порт для вас при вызове:
protected override void Dispose(bool disposing)
{
if (disposing && this.IsOpen)
{
this.internalSerialStream.Flush();
this.internalSerialStream.Close();
this.internalSerialStream = (SerialStream) null;
}
base.Dispose(disposing);
}
Если исключительное условие возникает в Dispose
обычно лучше иметь Dispose
выбрасывать исключение, а не завершать молча (лучше ли задушить исключение в какой-либо конкретной ситуации, будет зависеть от того, находится ли исключение в ожидании, и пока, увы, нет механизма, через который Dispose
может знать, когда это так). Потому что нет хорошего способа обработать исключение внутри Dispose
, часто хорошей идеей является обеспечение, когда это возможно, любых действий, которые могут пойти не так, если они будут выполнены в течение Dispose
будет сделано до Dispose
вызывается.
Если Dispose
были единственным методом очистки в обычном случае, тогда подавление исключений представляло бы значительный риск того, что могут возникнуть проблемы, но они останутся незамеченными. Наличие поддержки класса как Close
а также using
и клиенты звонят Close
в случае основной линии позволит снизить такой риск (если ожидается исключение, Dispose
позвонят без Close
и, следовательно, любые исключения при очистке будут подавлены, но код будет знать, что что-то пошло не так из-за ожидающего исключения; если до этого не произошло исключения Close
тот факт, что это не вызывается в Dispose
контекст очистки будет означать, что он может предполагать, что ни одно исключение не находится на рассмотрении, и поэтому может безопасно выбрасывать одно из своих)
Там не так много последовательности в практике обработки исключений Dispose
а также Close
, но я бы порекомендовал позвонить Close
на принципе. В зависимости от того, как Dispose
а также Close
реализованы, явно вызывая Close
перед неявным Dispose
может или не может быть полезным, но это должно быть в худшем случае безвредным. Учитывая возможность его полезности (если не в текущей версии класса, возможно, в будущей версии), я бы предложил это как общую привычку.