为什么每个 I/O 操作都创建一个新的 Stream 实例是一个好习惯? (C#)

Why is it a good practise to create a new instance of Stream per each I/O operation? (C#)

当使用以下结构实现 I/O 操作与 Stream class 时,有很多代码示例。像这样:

void ReadFileBro()
{
    using(var fs = File.Open("file.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite))
    {
         using(var sr = new StreamReader(fs))
         {
              //read file
         }
    }
}

当我们调用 ReadFileBro 时,它总是创建流具体实现的新实例。一旦重复操作,它就会销毁那些。

为什么我不能只创建一次 StreamReader 并将其保存为任何 class 的 属性 以供多次使用?

最近我部署了一个通过 SSH 进行客户端-服务器交互的小应用程序(使用 [SSH.NET][1])。这个(简化的)代码出错了:

class MyConnection
{
    ShellStream myStream = client.CreateShellStream(...);//it represents Stream between client and server, create it as single instance for each calling SendCommand

    void SendCommand(string command)
    {
        myStream.WriteLine(command);//send a command to server
        string readed = null;
        while (myStream.DataAvailable)
        {
            output = myStream.Read();//get data from server
        }
    }
}

但是这段代码运行良好:

class MyConnection
{
    void SendCommand(string command)
    {
        myStream.WriteLine(command);
        using(var myStream = client.client.CreateShellStream(...))
        { /* get data from server */ }
    }
}

怎么会这样?

在使用后关闭流并创建新流的主要原因是资源锁定和释放实际资源上的资源。一般来说,长期保存一个 IDisposable 对象是一种不好的做法。

创建新对象的成本非常低,并确保适当的资源管理。

编辑: 根据您的具体情况,您的方法很可能有效,但是,您应该正确实施 IDisposable。下面是一个高级示例

class MyConnection : IDisposable
{
    ShellStream myStream = client.CreateShellStream(...);//it represents Stream between client and server, create it as single instance for each calling SendCommand

    void SendCommand(string command)
    {
        myStream.WriteLine(command);//send a command to server
        string readed = null;
        while (myStream.DataAvailable)
        {
            output = myStream.Read();//get data from server
        }
    }

    public void Dispose()  
    {  
        Dispose(true);  
        GC.SuppressFinalize(this);  
    } 

    protected virtual void Dispose(bool disposing)  
    {  
        if (disposing)   
        {  
            // free managed resources  
            if myStream != null)  
            {  
                myStream .Dispose();  
                myStream = null;  
            }  
        }  
    }  
}

我认为建议您根据请求初始化流并关闭它,原因有两个:

  1. 您确保打开和关闭每个流。
  2. 您打开的流不多,这可能会导致问题。这在数据库连接中很常见。

Using 是创建一次性对象然后允许对该对象进行 运行 操作的简写。当它退出 using 块时,对象 运行s 它是 Dispose() 方法。

IDisposable 对于使用外部资源(例如文件、数据库等)的对象很重要,而且在相关对象访问非托管代码的情况下也非常重要。您的文件流对象无疑会访问较低级别的系统组件以访问文件并 read/write 访问它们。文件流实现 IDisposable 因为它的 Dispose() 方法将 运行 私有方法关闭它一直在访问的任何资源。这是为了帮助防止内存泄漏、锁定资源等。

没有实现 IDisposable 的对象通常在相关对象移出范围时失效,也就是说,代码执行已转移到另一个无法再访问该对象的方法实例。短时间后,这些类型的对象将被垃圾收集器标记为无效并自动清除,只要没有指向该对象的活动指针即可。

IDisposable不一样,因为我们访问的是GC无法访问的外部资源,但仍然占用应用程序内存。 Dispose() 方法确保这一切都是手动整理的。这就像吃完饭自己打扫 table 而不是让服务员来打扫。