如何从方法返回 Stream,知道它应该被释放?

问题描述 投票:0回答:4

我有一个以

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
并关闭它?

c# stream filestream using idisposable
4个回答
54
投票

当您从方法返回

IDisposable
时,您将处理它的责任交给了调用者。因此,您需要围绕流的整个使用声明您的
using
块,在您的情况下可能跨越
UploadFile
调用。

using (var s = GetFileStream())
    UploadFile(s);

24
投票

问题在于,一旦退出

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 对象,并让新的类实例跟踪您需要在循环结束时处理的所有内容。


5
投票

如果您有一个方法需要返回打开的文件流,那么该方法的所有调用者都需要负责处理返回的流,因为它无法在返回流之前处理该流。


-2
投票

我不确定如何阅读问题中的代码,因为

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ɔɯǝɹ指出的那样),事实上用户甚至需要知道它必须以任何特殊方式处置或处理......

© www.soinside.com 2019 - 2024. All rights reserved.