Есть ли лучший детерминированный шаблон утилизации, чем вложенное «использование»?

StackOverflow https://stackoverflow.com/questions/75722

  •  09-06-2019
  •  | 
  •  

Вопрос

В C#, если я хочу детерминированно очистить неуправляемые ресурсы, я могу использовать ключевое слово «using».Но для нескольких зависимых объектов это приводит к все большему и большему вложению:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
{
    using (BufferedStream bs = new BufferedStream(fs))
    {
        using (StreamReader sr = new StreamReader(bs))
        {
            // use sr, and have everything cleaned up when done.
        }
    }
}

В C++ я привык использовать деструкторы, чтобы сделать это следующим образом:

{    
    FileStream fs("c:\file.txt", FileMode.Open);
    BufferedStream bs(fs);
    StreamReader sr(bs);
    // use sr, and have everything cleaned up when done.
}

Есть ли лучший способ сделать это на С#?Или я застрял на нескольких уровнях вложенности?

Это было полезно?

Решение

Вам не нужно вкладывать несколько вариантов использования:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
    // all three get disposed when you're done
}

Другие советы

Вы можете объединить операторы using перед открывающими скобками следующим образом:

  using (StreamWriter w1 = File.CreateText("W1"))
  using (StreamWriter w2 = File.CreateText("W2"))
  {
      // code here
  }

http://blogs.msdn.com/ericgu/archive/2004/08/05/209267.aspx

Вы можете использовать этот синтаксис, чтобы немного упростить ситуацию:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
}

Это один из тех редких случаев, когда не использовать { } для всех блоков имеет смысл, ИМХО.

Вместо вложения операторов using вы можете просто записать вызовы .Dispose вручную, но в какой-то момент вы почти наверняка пропустите один из них.

Либо запустите FxCop, либо что-нибудь еще, что может гарантировать, что все экземпляры типа, реализующие IDisposable, имеют вызов .Dispose(), или разберитесь с вложенностью.

Я реализовал такие решения, как Майкл Медоузбыло раньше, но его StreamWrapper код не учитывает, если Dispose() методы, вызываемые для переменных-членов, по той или иной причине выдают исключение, последующее Dispose()es не будет вызываться, и ресурсы могут зависнуть.Более безопасный способ работы:

        var exceptions = new List<Exception>();

        try
        {
            this.sr.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        try
        {
            this.bs.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        try
        {
            this.fs.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        if (exceptions.Count > 0)
        {
            throw new AggregateException(exceptions);
        }
    }

вы можете опустить фигурные скобки, например:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
        // use sr, and have everything cleaned up when done.
}

или используйте обычный подход tryfinally:

FileStream fs = new FileStream("c:\file.txt", FileMode.Open);
BufferedStream bs = new BufferedStream(fs);
StreamReader sr = new StreamReader(bs);
try
{
        // use sr, and have everything cleaned up when done.
}finally{
   sr.Close(); // should be enough since you hand control to the reader
}

Это дает гораздо больший чистый плюс в строках кода, но ощутимый выигрыш в читабельности:

using (StreamWrapper wrapper = new StreamWrapper("c:\file.txt", FileMode.Open))
{
    // do stuff using wrapper.Reader
}

Здесь определен StreamWrapper:

private class StreamWrapper : IDisposable
{
    private readonly FileStream fs;
    private readonly BufferedStream bs;
    private readonly StreamReader sr;

    public StreamWrapper(string fileName, FileMode mode)
    {
        fs = new FileStream(fileName, mode);
        bs = new BufferedStream(fs);
        sr = new StreamReader(bs);
    }

    public StreamReader Reader
    {
        get { return sr; }
    }

    public void Dispose()
    {
        sr.Dispose();
        bs.Dispose();
        fs.Dispose();
    }
}

Приложив некоторые усилия, StreamWrapper можно было бы реорганизовать, чтобы сделать его более универсальным и пригодным для повторного использования.

Следует отметить, что обычно при создании потока на основе другого потока новый поток закрывает передаваемый.Итак, чтобы еще больше сократить ваш пример:

using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
    // all three get disposed when you're done
}

для этого примера предположим, что у вас есть:

файл с именем 1.xml в папке c:\

текстовое поле с именем textBox1 с включенными многострочными свойствами.

const string fname = @"c:\1.xml";

StreamReader sr=new StreamReader(new BufferedStream(new FileStream(fname,FileMode.Open,FileAccess.Read,FileShare.Delete)));
textBox1.Text = sr.ReadToEnd();

Оператор using представляет собой синтаксический сахар, который преобразуется в:

   try
   {
      obj declaration
      ...
   }
   finally
   {
      obj.Dispose();
   }

Вы можете явно вызвать Dispose для своих объектов, но это будет не так безопасно, поскольку, если один из них выдаст исключение, ресурсы не будут освобождены должным образом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top