我有一个以
FileStream
作为输入的方法。该方法在 for 循环内运行。
private void UploadFile(FileStream fileStream)
{
var stream = GetFileStream();
// do things with stream
}
我有另一种方法可以创建并返回
FileStream
:
private FileStream GetFileStream()
{
using(FileStream fileStream = File.Open(myFile, FileMode.Open))
{
//Do something
return fileStream;
}
}
现在,当我尝试访问返回的
ObjectDisposedException
时,第一个方法会抛出 FileStream
,可能是因为它已经关闭,因为我正在使用 using
来正确处理流。
如果我不使用
using
而是按如下方式使用它,则 FileStream
保持打开状态,并且循环的下一次迭代(在同一文件上操作)会抛出异常,告诉文件已在使用中:
private FileStream GetFileStream()
{
FileStream fileStream = File.Open(myFile, FileMode.Open);
//Do something
return fileStream;
}
如果我使用 try-finally 块,我在
finally
中关闭流,那么它也会抛出 ObjectDisposedException
。
如何有效返回
FileStream
并关闭它?
当您从方法返回
IDisposable
时,您将处理它的责任交给了调用者。因此,您需要围绕流的整个使用声明您的 using
块,在您的情况下可能跨越 UploadFile
调用。
using (var s = GetFileStream())
UploadFile(s);
问题在于,一旦退出
GetFileStream()
方法,FileStream 对象就会被释放,使其处于不可用状态。正如其他答案已经表明的那样,您需要从该方法中删除 using
块,并将 using
块放在调用此方法的任何代码周围:
private FileStream GetFileStream()
{
FileStream fileStream = File.Open(myFile, FileMode.Open);
//Do something
return fileStream;
}
using (var stream = GetFileStream())
{
UploadFile(stream);
}
但是,我想更进一步。您需要一种方法来保护
GetFileStream()
创建的流,防止草率的程序员可能在没有 using
块的情况下调用该方法,或者至少以某种方式强烈指示调用者需要包含此方法的结果带有 using
块。因此,我推荐这个:
public class FileIO : IDisposable
{
private FileStream streamResult = null;
public FileStream CreateFileStream(string myFile)
{
streamResult = File.Open(myFile, FileMode.Open);
//Do something
return streamResult;
}
public void Dispose()
{
if (streamResult != null) streamResult.Dispose();
}
}
using (var io = new FileIO())
{
var stream = io.CreateFileStream(myFile);
// loop goes here.
}
请注意,您不一定需要为此创建一个全新的类。您可能已经有一个适合此方法的类,您只需在其中添加 IDisposable 代码即可。最重要的是,您可以使用
IDisposable
作为向其他程序员发出的信号,表明该代码应该用 using
块包装。
此外,这还可以让您修改该类,以便您可以在循环之前创建一次 IDisposable 对象,并让新的类实例跟踪您需要在循环结束时处理的所有内容。
如果您有一个方法需要返回打开的文件流,那么该方法的所有调用者都需要负责处理返回的流,因为它无法在返回流之前处理该流。
我不确定如何阅读问题中的代码,因为
UploadFile
方法接收 fileStream
,但随后通过 stream
创建自己的 GetFileStream
并且根本不使用 fileStream
。
但我仍然有一个建议也可以解决类似的问题。 它称为“工厂隔离模式”(摘自 Gary McLean Hall 所著的《Adaptive Code via C#》一书) 这个想法是将对象的创建和销毁放在一起,但仍然允许以灵活的方式使用对象。所需要的只是 @frankmartin 原始
GetFileStream
方法的一点变化,只是我们扭转了局面,而不是让一次性对象逃脱,我们让“做某事”在:
private void With(Action<FileStream> do)
{
using (FileStream fileStream = File.Open(myFile, FileMode.Open))
{
do(fileStream);
}
}
然后你可以这样使用这个方法:
With(fileStream => UploadFile(fileStream);
这里用户不能忘记处置流(正如@oɔɯǝɹ指出的那样),事实上用户甚至需要知道它必须以任何特殊方式处置或处理......