如何 return 来自方法的流,知道它应该被处置?

How to return a Stream from a method, knowing it should be disposed?

我有一个将 FileStream 作为输入的方法。此方法在 for 循环中 运行。

private void UploadFile(FileStream fileStream)
{
    var stream = GetFileStream();
    // do things with stream
}

我有另一种创建和 returns FileStream 的方法:

private FileStream GetFileStream()
{
    using(FileStream fileStream = File.Open(myFile, FileMode.Open))
    {
        //Do something
        return fileStream;
    }
}

现在,当我尝试访问 returned FileStream 时,第一种方法抛出 ObjectDisposedException,可能是因为它已经关闭,因为我正在使用“using”来正确处理流。

如果我不使用 "using" 而是按如下方式使用它,则 FileStream 保持打开状态并且循环的下一次迭代(对同一文件进行操作)会抛出一个异常,告知该文件已经存在使用中:

private FileStream GetFileStream()
{
    FileStream fileStream = File.Open(myFile, FileMode.Open);
    //Do something
    return fileStream;
}

如果我使用 try-finally 块,我在 finally 中关闭流,那么它也会抛出 ObjectDisposedException.

如何有效地return文件流并关闭它?

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

当您从一个方法中 return 一个 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 = FileIO())
{
    var stream = io.CreateFileStream(myFile);

    // loop goes here.
}

请注意,您不一定需要为此创建一个全新的 class。您可能已经有了适合此方法的 class,您可以在其中添加 IDisposable 代码。最主要的是,您可以使用 IDisposable 作为向其他程序员发出的信号,即这段代码应该用 using 块包装。

此外,这使您可以修改 class 以便您可以在循环之前创建一次 IDisposable 对象,并让新的 class 实例跟踪您需要的所有内容在循环结束时处理。

我不确定如何阅读问题中的代码,因为 UploadFile 方法接收 fileStream,但随后通过 GetFileStream 创建自己的 stream 和根本不使用 fileStream

但我仍然有一个可能也能解决类似问题的建议。 它称为 'Factory Isolation Pattern'(来自 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ɔɯǝɹ指出的那样),事实上用户甚至需要知道它必须被处理或处理以任何特殊方式...