Метод возвращает IDisposable - я должен избавиться от результата, даже если он ничему не присвоен?

Это походит на довольно простой вопрос, но я не мог найти этот конкретный пример использования после некоторого поиска вокруг.

Предположим, что у меня есть простой метод, который, скажем, определяет, открыт ли файл некоторым процессом. Я могу сделать это (не 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 избавиться от него сразу же?

5
задан matt 14 June 2010 в 16:56
поделиться

4 ответа

Да, вам определенно следует избавиться от FileStream . В противном случае поток останется открытым, и файл нельзя будет использовать, пока финализатор не очистит его.

Важным здесь является право собственности : для File.Open предполагается, что вызывающий «владеет» потоком, возвращенным ему - и если вы владеете чем-то, что реализует ] IDisposable , вы обязаны избавиться от него.

Сравните это с ситуацией Image.FromStream : в этом случае вы передаете поток, и Image затем предполагает, что он владеет этим потоком . Вы не должны сами закрывать поток, в этом случае вы должны удалить изображение, когда вы закончите, и он избавится от потока.

Вызов статического метода, который возвращает что-то одноразовое , почти всегда предполагает, что вызывающий объект становится владельцем ресурса. То же самое с конструкторами (которые фактически являются статическими методами).

Ирония в том, что если вы не удалите поток, возвращаемый File.Open , вы обнаружите, что это файл пригодный для использования - в то же время, что делает его непригодным для использования до неопределенного времени.

15
ответ дан 18 December 2019 в 07:28
поделиться

Если метод возвращает 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 освобождать управляемые ресурсы - как только вы закончите с ними вместо того, чтобы удерживать ресурсы до тех пор, пока в будущем не будет вызван финализатор.

5
ответ дан 18 December 2019 в 07:28
поделиться

Когда вы вызываете любой метод, который что-то возвращает, создается экземпляр этого чего-то. То, что вы на самом деле его не фиксируете, не делает его менее «там».Следовательно, в случае объекта IDisposable объект создается вызываемым вами методом, несмотря на то, что вы ничего с ним не делаете. Так что да, избавиться от него все равно нужно. Первый подход с оператором using кажется, что он должен работать.

0
ответ дан 18 December 2019 в 07:28
поделиться

Да, вы не хотите оставлять FileStream открытым. Во-первых, после этого вы даже не сможете открыть файл самостоятельно. Вызов Close () достаточно хорош, но использование using, вероятно, является предпочтительным шаблоном.

Однако с вашим кодом есть гораздо большая проблема. Он не может надежно работать в Windows. Типичный сценарий:

  • Вызов File.Open () завершается успешно. Вы закрываете его
  • Планировщик Windows прерывает ваш поток
  • Другой поток в другом процессе получает шанс запустить, он открывает файл
  • Ваш поток восстанавливает процессор и продолжает работу после File.Open ( ) call
  • Вы открываете файл, полагая, что он будет работать, поскольку IsOpen () вернула false.

Кабум.

Никогда не пишите такой код, отказ чрезвычайно трудно диагностировать. Открывайте файл только тогда, когда вы готовы начать его чтение или запись. И не закрывайте его, пока не закончите.

Дополнительный бонус: теперь очевидно, что вы хотите использовать оператор using.

1
ответ дан 18 December 2019 в 07:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: