我有一个主窗口,用户单击该按钮处理选定的输入文件。按钮触发的代码块打开输入文件,输出文件和日志文件。同一个代码块一次读取一行输入文件的行,对每行的字段进行一些操作,然后将相应的行写入输出文件。错误消息将写入日志文件。处理单个文件可能需要几分钟。
如果用户不耐烦,我希望能够优雅地关闭3个文件,并使用红色的“X”按钮关闭主窗口。我有一个事件处理程序捕获单击“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
答案 0 :(得分:3)
当然它不起作用。这三个变量是方法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语句是因为在处理数据时,如果出现异常,它也会关闭并销毁定义行中使用的变量
答案 1 :(得分:0)
它只是一个范围混合:
第一步:将其放入类范围
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();
和朋友
答案 2 :(得分:0)
您正在事件处理程序中创建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。 IDispospos是具有传染性的,并通过堆栈向上传播,直到您保证处置。如果你只使用该方法范围内的对象,你将为自己节省很多乐趣。