根据父母一次性返回一次性

时间:2018-05-14 00:07:06

标签: c# dispose using system.io.compression

以下是我正在处理的代码的一部分(为了清晰起见而修改):

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),它将文件作为字节数组返回。显然,消耗大量内存,我想使用流而不是。

2 个答案:

答案 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实例已正确处理,您就应该能够减少使用。