Закрыть и утилизировать - куда звонить? - PullRequest
148 голосов
/ 14 сентября 2008

Прочитав темы Достаточно ли SqlCommand.Dispose? и Закрытие и удаление службы WCF Мне интересно узнать о таких классах, как SqlConnection или одном из нескольких классов, наследуемых от Stream класс имеет ли значение, если я закрываю Dispose, а не Close?

Ответы [ 7 ]

173 голосов
/ 14 сентября 2008

Я хочу прояснить эту ситуацию.

В соответствии с рекомендациями Microsoft рекомендуется использовать метод Close там, где это необходимо. Здесь - цитата из Руководства по проектированию фреймворка

Подумайте о предоставлении метода Close(), в дополнение к Dispose(), если закрытие является стандартной терминологией в области. При этом важно, чтобы вы сделали реализацию Close идентичной Dispose ...

В большинстве случаев Close и Dispose методы эквивалентны. Основное различие между Close и Dispose в случае SqlConnectionObject составляет:

Приложение может звонить Close больше чем один раз. Нет исключения генерироваться.

Если вы вызвали Dispose метод SqlConnection состояние объекта будет сброс. Если вы попытаетесь позвонить любой метод утилизации SqlConnection объект, вы получите исключение.

Это говорит:

  • Если вы используете объект подключения один время, используйте Dispose.
  • Если объект подключения необходимо использовать повторно, используйте метод Close.
23 голосов
/ 14 сентября 2008

Как обычно, ответ: это зависит. Различные классы реализуют IDisposable по-разному, и вы должны провести необходимое исследование.

Что касается SqlClient, рекомендуемая практика заключается в следующем:

using (SqlConnection conn = /* Create new instance using your favorite method */)
{
    conn.Open();
    using (SqlCommand command = /* Create new instance using your favorite method */)
    {
        // Do work
    }
    conn.Close(); // Optional
}

Вы должны звонить Dispose (или Close*) по соединению! не подождите, пока сборщик мусора не очистит ваше соединение, это будет связывать соединения в пуле до следующего цикла GC (по крайней мере). Если вы вызываете Dispose, то нет необходимости вызывать Close, и поскольку конструкция using позволяет легко обрабатывать Dispose правильно, на самом деле нет причин для вызова Close.

Соединения автоматически объединяются, и вызов Dispose / Close для соединения физически не закрывает соединение (при нормальных обстоятельствах). Не пытайтесь реализовать свой собственный пул. SqlClient выполняет очистку соединения при его извлечении из пула (например, восстановление контекста базы данных и параметров соединения).

*, если вы звоните Close, убедитесь, что вы делаете это безопасным для исключения способом (т. Е. В блоке catch или finally).

11 голосов
/ 14 сентября 2008

Вам необходимо вызвать Dispose ()!

Dispose () предназначен для вызова разработчиком, сборщик мусора вызывает Finalize (). Если вы не вызываете Dispose () на своих объектах, любые неуправляемые ресурсы, которые они использовали, не будут утилизироваться, пока сборщик мусора не придет и не завершит вызовы для них (и кто знает, когда это произойдет).

Этот сценарий называется недетерминированным финализацией и является обычной ловушкой для разработчиков .net. Если вы работаете с объектами, которые реализуют IDisposable, тогда вызовите Dispose () для них!

http://www.ondotnet.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html?page=last

Хотя может быть много случаев (например, в SqlConnection), когда вы вызываете Disponse () для какого-либо объекта, и он просто вызывает Close () для своего соединения или закрывает дескриптор файла, почти всегда лучше всего звонить Dispose ()! если вы не планируете повторно использовать объект в самом ближайшем будущем.

10 голосов
/ 14 сентября 2008

Для SqlConnection, с точки зрения самого соединения, они эквивалентны. Согласно Reflector, Dispose() вызывает Close(), а также выполняет несколько дополнительных операций освобождения памяти - в основном, устанавливая члены равными нулю.

Для Stream они фактически эквивалентны. Stream.Dispose() просто вызывает Close ().

6 голосов
/ 14 сентября 2008

Этот быстрый совет стал длинным ответом. К сожалению.

Как отметил Тайлер в своем хорошем ответе, вызов Dispose() - отличная практика программирования. Это связано с тем, что этот метод должен объединять все необходимое освобождение ресурсов, чтобы не было ненужных открытых ресурсов. Например, если вы записали какой-то текст в файл и не смогли закрыть файл (освободить ресурс), он останется открытым, и никто больше не сможет писать в него, пока GC не придет и не сделает то, что вы должны иметь. сделано.

Теперь в некоторых случаях будут "финализировать" методы, более специфичные для класса, с которым вы имеете дело, например, StreamWriter.Close(), который переопределяет TextWriter.Close(). Действительно, они обычно больше подходят для ситуации: например, Close() StreamWriter сбрасывает поток и нижележащий кодер перед Dispose() обработкой объекта! Круто!

Тем не менее, просматривая MSDN, вы обнаружите, что даже Microsoft иногда смущается множеством приближенных и распорядителей. На этой веб-странице , например, в некоторых примерах Close() вызывается перед неявным Dispose() (см. с использованием оператора , если вы не понимаете, почему он неявный), и в в частности, они не беспокоятся. С чего бы это? Я тоже был озадачен.

Причина, по которой я понял (и, подчеркиваю, это оригинальное исследование и я, безусловно, могу потерять репутацию, если я ошибаюсь), заключается в том, что Close() может потерпеть неудачу, приводя к исключению, в то время как оставив ресурсы открытыми, в то время как Dispose() наверняка освободит их . Вот почему a Dispose() всегда должен охранять Close() вызов (извините за каламбур).

MyResource r = new MyResource();

try {
  r.Write(new Whatever());

  r.Close()
finally {
  r.Dispose();
}

И да, я думаю, Microsoft воспользовалась этим примером. Возможно, эта временная метка никогда не будет сброшена в файл.

Завтра я исправляю свой старый код.

Редактировать: извините, Браннон, я не могу прокомментировать ваш ответ, но вы уверены, что это хорошая идея - позвонить Close() на finally блок? Я предполагаю, что исключение из этого может разрушить остальную часть блока, который, вероятно, будет содержать важный код очистки.

Ответ Браннону: отлично, просто не забудьте позвонить Close(), когда это действительно необходимо (например, при работе с потоками - мало что знаете о соединениях SQL в .NET).

2 голосов
/ 19 июня 2017

Обычно мы сталкиваемся с проблемами в Close (), Abort () и Dispose (), но позвольте мне рассказать вам разницу между ними.

1) ABORT: - Я не буду предлагать использовать это, потому что, когда вызывается abort, клиент удаляет соединение, не сообщая серверу, поэтому сервер будет ждать некоторое время (приблизительно 1 мин). Если у вас массовый запрос, вы не можете использовать abort (), поскольку это может привести к превышению времени ожидания для вашего ограниченного пула соединений.

2) Close: - Закрыть - это очень хороший способ закрыть соединение, потому что при закрытии соединения он вызывает сервер и подтверждает, что сервер тоже закрывается на этой стороне.

Здесь, еще одна вещь, чтобы посмотреть. В некоторых случаях, если генерируется ошибка, тогда это не очень хороший способ написать код, наконец, в connection.close (), потому что в это время будет нарушено состояние связи.

3) Утилизация: - Это один из типов закрытия, но после закрытия соединения вы не сможете открыть его снова.

Так что попробуйте так,

private void CloseConnection(Client client)
    {
        if (client != null && client.State == CommunicationState.Opened)
        {
            client.Close();
        }
        else
        {
            client.Abort();
        }
    }
2 голосов
/ 10 июля 2010

Введите iDisposable, и вызовите dispose для этого. Это вызовет любой метод, сконфигурированный как реализующий «iDisposable.Dispose», независимо от того, как названа функция.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...