Это походит на довольно простой вопрос, но я не мог найти этот конкретный пример использования после некоторого поиска вокруг.
Предположим, что у меня есть простой метод, который, скажем, определяет, открыт ли файл некоторым процессом. Я могу сделать это (не 100% правильно, но довольно хорошо) с этим:
public bool IsOpen(string fileName)
{
try
{
File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
}
catch
{
// if an exception is thrown, the file must be opened by some other process
return true;
}
}
(очевидно, это не лучший или даже корректный способ определить это - Файл. Открытый выдает много различных исключений, все с различными значениями, но это работает на этот пример),
Теперь File.Open
назовите возвраты a FileStream
, и FileStream
реализации IDisposable. Обычно мы хотели бы перенести использование любого FileStream
инстанцирования в блоке использования для проверки от них избавляются правильно. Но что происходит в случае, где мы ничему на самом деле не присваиваем возвращаемое значение? Все еще необходимо избавиться FileStream
, как так:
try
{
using (File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None));
{ /* nop */ }
}
catch
{
return true;
}
Если я создаю a FileStream
экземпляр и избавляется от этого?
try
{
using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None));
}
...
Или они совершенно не нужны? Мы можем просто звонить File.Open
и не присваивают его чему-нибудь (первый пример кода) и позволяют GC избавиться от него сразу же?
Да, вам определенно следует избавиться от FileStream
. В противном случае поток останется открытым, и файл нельзя будет использовать, пока финализатор не очистит его.
Важным здесь является право собственности : для File.Open
предполагается, что вызывающий «владеет» потоком, возвращенным ему - и если вы владеете чем-то, что реализует ] IDisposable
, вы обязаны избавиться от него.
Сравните это с ситуацией Image.FromStream
: в этом случае вы передаете поток, и Image
затем предполагает, что он владеет этим потоком . Вы не должны сами закрывать поток, в этом случае вы должны удалить изображение, когда вы закончите, и он избавится от потока.
Вызов статического метода, который возвращает что-то одноразовое , почти всегда предполагает, что вызывающий объект становится владельцем ресурса. То же самое с конструкторами (которые фактически являются статическими методами).
Ирония в том, что если вы не удалите поток, возвращаемый File.Open
, вы обнаружите, что это файл пригодный для использования - в то же время, что делает его непригодным для использования до неопределенного времени.
Если метод возвращает IDisposable
, я лично всегда помещал бы его в , используя
блок . Даже если я ничему не присваиваю возвращаемое значение.
Даже если вы не присвоите его переменной, одноразовый объект все равно будет создан. Dispose
- это не , который будет вызываться автоматически. Единственное различие будет заключаться в том, что возвращенный объект сразу станет подходящим для сборки мусора, поскольку на него нет (сильных) ссылок.
Сборщик мусора не вызывает автоматически Dispose
, когда он освобождает объект. Однако большинство IDisposable
типов предоставляют финализатор (который будет вызываться непосредственно перед тем, как сборщик мусора освободит объект), который вызывает Dispose
в качестве резервной стратегии (подстраховки) - изучите IDisposable шаблон , чтобы увидеть, как это делается:
~SomeClass // <-- the finalizer method will usually call Dispose;
{ // but you have no control over when it will be called!
Dispose(false);
}
Помните, что вы не знаете, когда запустится сборщик мусора (потому что он недетерминирован). Следовательно, вы также не знаете, когда будет вызван метод финализатора. И из-за этого - если вы не вызвали Dispose
явно (самостоятельно или с помощью блока using
) - вы не знаете , когда он будет вызван финализатором.
В этом преимущество явного вызова Dispose
: Вы можете освободить ресурсы - или, по крайней мере, позволить GC освобождать управляемые ресурсы - как только вы закончите с ними вместо того, чтобы удерживать ресурсы до тех пор, пока в будущем не будет вызван финализатор.
Когда вы вызываете любой метод, который что-то возвращает, создается экземпляр этого чего-то. То, что вы на самом деле его не фиксируете, не делает его менее «там».Следовательно, в случае объекта IDisposable объект создается вызываемым вами методом, несмотря на то, что вы ничего с ним не делаете. Так что да, избавиться от него все равно нужно. Первый подход с оператором using кажется, что он должен работать.
Да, вы не хотите оставлять FileStream открытым. Во-первых, после этого вы даже не сможете открыть файл самостоятельно. Вызов Close () достаточно хорош, но использование using, вероятно, является предпочтительным шаблоном.
Однако с вашим кодом есть гораздо большая проблема. Он не может надежно работать в Windows. Типичный сценарий:
Кабум.
Никогда не пишите такой код, отказ чрезвычайно трудно диагностировать. Открывайте файл только тогда, когда вы готовы начать его чтение или запись. И не закрывайте его, пока не закончите.
Дополнительный бонус: теперь очевидно, что вы хотите использовать оператор using.