Есть ли лучший детерминированный шаблон удаления, чем вложенные «использующие»? - PullRequest
20 голосов
/ 16 сентября 2008

В 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.
}

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

Ответы [ 10 ]

40 голосов
/ 16 сентября 2008

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

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
}
8 голосов
/ 16 сентября 2008

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

  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

3 голосов
/ 16 сентября 2008

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

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

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

1 голос
/ 17 сентября 2008

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

        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);
        }
    }
1 голос
/ 16 сентября 2008

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

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

0 голосов
/ 20 мая 2011

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

файл с именем 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();
0 голосов
/ 17 сентября 2008

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

using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
    // all three get disposed when you're done
}
0 голосов
/ 16 сентября 2008

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

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

0 голосов
/ 16 сентября 2008

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

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.
}

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

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
}
0 голосов
/ 16 сентября 2008

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

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

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

...