以下是我正在处理的代码的一部分(为了清晰起见而修改):
public Stream getMyArchiveStream(string archivepath)
{
using(var archive = ZipFile.OpenRead(_filepath))
{
var entry = archive.GetEntry(archivepath);
return entry.Open();
}
}
public void useMyArchiveStream()
{
using(var myStream = getMyArchiveStream("test.path"))
{
//Do stuff
}
}
现在失败了,因为 archive 正在 getMyArchiveStream 的出口处处理,这会阻止使用 myStream 。
处理 myStream 时,有没有办法处理存档?
另一种方法是让 archive 保持打开并使包含类成为一次性,但这在可用性方面有其自身的缺点。
背景:
我创建了一个简单的打包类(Simpler,至少是System.IO.Packaging),它将文件作为字节数组返回。显然,消耗大量内存,我想使用流而不是。
答案 0 :(得分:0)
简单地说,你正在采取你不能采取的捷径。正如您的问题标题已经完全说明的那样,您正试图在您处置的对象上使用依赖的对象。
您要做的是打开一个文件,从中读取一些关于其内部的信息(不是实际的内部结构),然后关闭文件, 然后尝试使用该信息实际从该文件中读取。这是不可能的; 该文件已经关闭。就像你不能从自己的using
块内部返回一个一次性对象而没有一个处理过的,因此无法使用的对象,你显然也可以& #39;在一次性物品上返回依赖的东西。
因此,基本上,getMyArchiveStream
函数背后的整个思维过程都是有缺陷的。你根本不应该有这个功能。你只需要制作这样的其他功能:
public void UseMyArchiveStream()
{
using(var archive = ZipFile.OpenRead(_filepath))
{
var entry = archive.GetEntry("test.path");
using(var myStream = entry.Open())
{
//Do stuff
}
}
}
另一种选择确实是让archive
保持开放......但是当mjwills发表评论时,还有另一种方法可以做你想要的事情,并且要给UseMyArchiveStream
{}一个Action<>
或Func<>
作为参数。这字面意思是&#34;做东西&#34;上面代码中的注释被调用您作为参数提供的任何函数替换:
public void UseMyArchiveStream(String zipPath, String entryName, Action<Stream, String> doStuff)
{
using (var archive = ZipFile.OpenRead(zipPath))
{
var entry = archive.GetEntry(entryName);
using (var myStream = entry.Open())
{
doStuff(myStream, entry.FullName);
}
}
}
使用函数void SaveStreamToFile(Stream file, String filename)
演示:
UseMyArchiveStream(_filepath, "test.path", (str, nm) => SaveStreamToFile(str, nm));
使用Func<>
,您可以进行重载以提供返回值。 <>
中的最后一个参数始终是返回类型。但您可以轻松使用泛型来使其依赖于调用输入:
public T UseMyArchiveStream<T>(String zipPath, String entryName, Func<Stream, String, T> doStuff)
{
using (var archive = ZipFile.OpenRead(zipPath))
{
var entry = archive.GetEntry(entryName);
using (var myStream = entry.Open())
{
return doStuff(myStream, entry.FullName);
}
}
}
您可以以相同的方式调用此方法,仅使用返回值而不是void
的函数。用Boolean DostuffWithFile(Stream file, String entryName)
展示:
Boolean ok = UseMyArchiveStream(_filepath, "test.path", (str, nm) => DostuffWithFile(str, nm));
请注意,您调用的函数不必与参数的确切签名匹配。您可以用这种调用方式用本地数据完美地替换缺少的参数。
使用Boolean DostuffWithFile(Stream file, String entryName, Boolean someOption, String outputFolder)
展示:
Boolean ok = UseMyArchiveStream(_filepath, "test.path", (str, nm) => DostuffWithFile(str, nm, true, _savePath));
只要UseMyArchiveStream
需要提供的输入只是=>
之前的部分,这将有效。当然,你可以随心所欲地处理这些争论;你甚至可以给函数提供整个ZipArchiveEntry
对象,甚至可能只提供源ZipFile
,这样你就可以随心所欲地做任何事情。
此方法的唯一缺点是您无法实际命名Action<>
或Func<>
的组件,因此,在这种情况下,您无法从中了解UseMyArchiveStream
的函数签名,String
的{{1}}参数是否会收到Func<Stream, String, T>
或entry.Name
。这同样适用于给出相同类型的多个参数;如果你在entry.FullName
中有五个布尔选项,那么你可能很难记住哪个是哪个,而不必每次都查看代码。因此,请务必在函数注释中准确记录,以避免以后混淆。
答案 1 :(得分:-1)
了解调用dispose的原因非常重要,因此您必须知道何时调用它,何时调用它。 Dispose and Finalize密切相关。
Finalize就是确保GC可以释放非托管资源(Filehandles,Networkhandles,Natively Adressed Memory Space)。但是,虽然确定GC 将运行,但时确定确定。实际上,如果它仅在应用程序关闭时运行,那么这是大多数实现所针对的理想情况。
Dispose就是确保Finalization是确定性的。当你处置时,你不再完成任务。你很早就完成了Finalization的工作。你可以随心所欲地做到这一点,而不是当GC能够解决它时。
有很多细节,但对我而言,归结为有两种情况可以实现IDisposeable: *您直接处理非托管resoruce。在这种情况下,您首先进行终结。然后将Dispose作为便利/可用性功能。你不会真正遇到这种情况,因为大多数都是由Framework程序员处理的。 *您处理任何实现IDisposeable的类,仅用于&#34; relay&#34; Dispose调用所有cotained实例。很容易95%的所有Disposeable电话就是这个。
使用ZipArchive,我的猜测是它实现IDisposeable的主要原因,是因为它必须包含的文件句柄。 Entry可能只是实现它,因为它包含对ZipArchive实例的引用。因此它应该转发呼叫。虽然ZipArchiveEntry中还有其他东西需要处理,但我认为这不太可能。因此,只要您确定ZipArchive实例已正确处理,您就应该能够减少使用。