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