【问题标题】:Need to close filestreams that were opened in another context of the same app需要关闭在同一应用程序的另一个上下文中打开的文件流
【发布时间】:2013-12-07 09:31:07
【问题描述】:

我有一个主窗口,用户单击一个按钮来处理选定的输入文件。按钮触发的代码块打开输入文件、输出文件和日志文件。相同的代码块一次读取输入文件的一行,对每行中的字段执行一些操作,然后将相应的行写入输出文件。错误消息被写入日志文件。处理单个文件可能需要几分钟时间。

如果用户不耐烦并使用红色的“X”按钮关闭主窗口,我希望能够优雅地关闭这 3 个文件。我有一个捕获单击“X”的事件处理程序,但我无法引用我要关闭的 3 个文件流,因为它们是在事件处理程序的上下文之外创建的。如何将 3 个文件流传递给处理程序?

namespace less
{
   public partial class MainWindow : Window
   {
        ...

        private static string filename;

        ...

      public MainWindow()
      {

            InitializeComponent();

            // string filename is chosen in this block of code

      }

    private void convertData_Click(object sender, RoutedEventArgs e)
        {
            // This is invoked when user clicks the "Process Data" button

            System.IO.StreamReader inputFile = new System.IO.StreamReader(filename);
            System.IO.StreamWriter outputFile = new System.IO.StreamWriter(filename + ".less.csv");
            System.IO.StreamWriter logFile = new System.IO.StreamWriter(filename + ".log");

                     ....

               <process all the data>

                     ....

           inputFile.Close();  // <- this works
           outputFile.Close();  // <- this works
           logFile.Close();  // <- this works

           this.Close();
         }

    private void MainWindow_Closed(object sender, EventArgs e)
        {

           // This is the event handler invoked when the red "X" is clicked.

           inputFile.Close(); // <- this (and following) do not work
           outputFile.Close();
           logFile.Close();
        }

   } // closes public partial class MainWindow : Window

} // closes namespace

【问题讨论】:

  • 您是否尝试过处理对象,而不仅仅是关闭它们?创建实现 IDisposable 的对象时,您应该使用 using 语句。

标签: c# filestream


【解决方案1】:

当然不行。这三个变量是方法convertData_Click 的本地变量,因此不能在该方法之外引用。如果您真的想这样做,您需要将三个变量移动到变量filename 的同一级别。它们成为全局类级变量,可以在该类的任何方法中引用。

说过,我真的建议避免这种情况。 FileStream 应该在需要时声明、使用、关闭和处置,并且在表单的生命周期内不要保持打开状态

namespace less
{
   public partial class MainWindow : Window
   {
        ...

       // Now these are global class level variables...
       // But again, don't do that.....
       private StreamReader inputFile;
       private StreamWriter outputFile;
       private StreamWriter logFile;

       private static string filename;

        ...
   }

更好的方法是

private void convertData_Click(object sender, RoutedEventArgs e)
{
   using(StreamReader inputFile = new System.IO.StreamReader(filename))
   using(StreamWriter outputFile = new System.IO.StreamWriter(filename + ".less.csv"))
   using(StreamWriter logFile = new System.IO.StreamWriter(filename + ".log"))
   {
        ....
        <process all the data>
        ....
   }
}

最好使用 using 语句,因为它在处理数据时也会关闭并销毁定义行中使用的变量

【讨论】:

  • 那么,史蒂夫,如果我使用“using”语句,我就不用担心关闭其他地方的文件了吗?
  • 不,这个问题不是简单的通过using语句就能解决的。上面的代码是连续的,因此,如果您的用户按下关闭按钮,代码仍然会运行,直到所有数据都被处理,因为您的代码直到最后才会看到 Close 事件。您应该使用线程(或 BackgroundWorker)在此代码之外委派文件处理并释放应用程序的 UI。然后,如果您的用户按下关闭按钮,则您向 BackgroundWorker 发出信号以停止其处理并关闭文件
【解决方案2】:

这只是一个范围混淆:

第一步:把这个放到类作用域中

System.IO.StreamReader inputFile;
System.IO.StreamWriter outputFile;
System.IO.StreamWriter logFile;

第二步:替换

System.IO.StreamReader inputFile = new System.IO.StreamReader(filename);
System.IO.StreamWriter outputFile = new System.IO.StreamWriter(filename + ".less.csv");
System.IO.StreamWriter logFile = new System.IO.StreamWriter(filename + ".log");

inputFile = new System.IO.StreamReader(filename);
outputFile = new System.IO.StreamWriter(filename + ".less.csv");
logFile = new System.IO.StreamWriter(filename + ".log");

第三步:确保文件真的打开了!要么使用 try/catch 构造,要么使用

if (inputFile != null) inputFile.Dispose();

和朋友

【讨论】:

    【解决方案3】:

    您正在事件处理程序中创建 FileStream 对象,因此它们仅存在于该事件处理程序的范围内,然后它们被垃圾收集器标记为收集。您已经在处理程序的末尾关闭了流,因此即使您尝试在方法之外访问它们,它们也会被关闭,因此这归结为与 C# 中的 IDisposable 模式相关的几个关键点.

    文件流实现 IDisposable 接口。这意味着他们保证有一个 Dispose 方法。 Dispose 是一种清除对象需要的任何资源的方法。例如,对于 FileStreams,Dispose 方法会关闭流。 using 关键字可以在任何实现 IDisposable 的东西上执行。关键字用作:

    using(StreamReader reader = new StreamReader(filename))
    {
        // Reading the stream
    }
    

    您可以在脑海中替换为:

    try
    {
        StreamReader reader = new StreamReader(filename);
       // Reading the stream
    }
    finally
    {
        if(reader != null)
        {
            reader.Dispose();
        }
    }
    

    这实际上意味着您不再需要担心关闭流,因为 Dispose 方法将被调用,即使在读取期间抛出异常也是如此。方便,不是吗?

    如果您坚持要在事件处理程序之外关闭流,则需要将流定义移出类范围。但是,这并不能让您摆脱 IDisposable 的乐趣,因为每当您有一个包含 IDisposable 对象的类时,该类就需要实现 IDisposable。并且该类的 Dispose 方法应该在 Disposable 成员对象上调用 Dispose。 IDisposable 具有传染性,并通过堆栈向上传播,直到您保证处置。如果您只使用该方法范围内的对象,您将为自己节省很多乐趣。

    【讨论】:

      猜你喜欢
      • 2015-09-15
      • 1970-01-01
      • 1970-01-01
      • 2013-03-06
      • 2015-08-30
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      • 2015-08-11
      相关资源
      最近更新 更多