是否有比嵌套“使用”更好的确定性处置模式?

发布于 2024-07-04 04:01:16 字数 648 浏览 11 评论 0原文

在 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# 中是否有更好的方法来执行此操作? 或者我是否陷入了多层嵌套?

In C#, if I want to deterministically clean up non-managed resources, I can use the "using" keyword. But for multiple dependent objects, this ends up nesting further and further:

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

In C++, I'm used to being able to use destructors to do it like this:

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

Is there a better way in C# to do this? Or am I stuck with the multiple levels of nesting?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

逆光飞翔i 2024-07-11 04:01:16

using 语句是语法糖,可转换为:

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

您可以在对象上显式调用 Dispose,但它不会那么安全,因为如果其中一个抛出异常,则资源将无法正确释放。

The using statement is syntactic sugar that converts to:

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

You can explicitly call Dispose on your objects, but it won't be as safe, since if one of them throws an exception, the resources won't be freed properly.

云淡风轻 2024-07-11 04:01:16

对于此示例,我们假设您有:

c:\ 下名为 1.xml 的文件

名为 textBox1 的文本框,且多行属性设置为 ON。

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();

for this example let us assume you have:

a file named 1.xml under c:\

a textbox named textBox1, with the multi-line properties set ON.

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();
凉城已无爱 2024-07-11 04:01:16

应该注意的是,通常当基于另一个流创建流时,新流将关闭传入的流。因此,为了进一步减少示例:

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

It should be noted that generally when creating stream based off another stream the new stream will close the one being passed in. So, to further reduce your example:

using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
    // all three get disposed when you're done
}
夏花。依旧 2024-07-11 04:01:16

这使得代码行数增加了很多,但可读性有了明显的提高:

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 可以被重构为更通用和可重用。

This makes for a much larger net plus in lines of code, but a tangible gain in readability:

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

Where StreamWrapper is defined here:

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();
    }
}

With some effort, StreamWrapper could be refactored to be more generic and reusable.

夜吻♂芭芘 2024-07-11 04:01:16

您可以手动写出 .Dispose 调用,而不是嵌套 using 语句 - 但您几乎肯定会在某些时候错过一个。

运行 FxCop 或其他可以确保所有 IDisposable 实现类型实例都有 .Dispose() 调用的程序,或者处理嵌套。

Instead of nesting using statements, you can just write out the .Dispose calls manually - but you'll almost certainly miss one at some point.

Either run FxCop or something else that can make sure that all IDisposable-implementing type instances have a .Dispose() call, or deal with the nesting.

睫毛溺水了 2024-07-11 04:01:16

您可以省略花括号,例如:

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
}

you can omit the curly braces, like:

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

or use the regular try finally approach:

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
}
花开雨落又逢春i 2024-07-11 04:01:16

我已经实现了像 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);
        }
    }

I have implemented solutions like Michael Meadows's before, but his StreamWrapper code doesn't take into account if the Dispose() methods called on the member variables throw an exception for one reason or another, the subsequent Dispose()es will not be called and resources could dangle. The safer way for that one to work is:

        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);
        }
    }
笑,眼淚并存 2024-07-11 04:01:16

您可以将 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

You can put using statements together before the opening braces like so:

  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

德意的啸 2024-07-11 04:01:16

您可以使用此语法来压缩一些内容:

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

恕我直言,这是一种罕见的情况,不对所有块使用 { } 是有意义的。

You could use this syntax to condense things down a bit:

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

This is one of those rare occasions where not using { } for all blocks makes sense IMHO.

温柔一刀 2024-07-11 04:01:16

您不必嵌套多个 using:

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
}

在 .NET Core 中,有一个新的 using 语句,它允许您省去括号,并且处理发生在当前作用域的末尾:

void MyMethod()
{
    using var fs = new FileStream("c:\file.txt", FileMode.Open);
    using var bs = new BufferedStream(fs);
    using var sr = new StreamReader(bs);
    // all three are disposed at the end of the method
}

You don't have to nest with multiple usings:

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
}

In .NET Core, there's a new using statement which allows you to dispense with the parentheses, and the disposal happens at the end of the current scope:

void MyMethod()
{
    using var fs = new FileStream("c:\file.txt", FileMode.Open);
    using var bs = new BufferedStream(fs);
    using var sr = new StreamReader(bs);
    // all three are disposed at the end of the method
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文