return 一次性取决于 parent 一次性

return disposable depending on parent disposable

这是我正在处理的部分代码(为清楚起见进行了修改):

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

另一种方法是让 archive 打开并使包含 class 的内容成为一次性的,但这在可用性方面有其自身的缺点。

背景:

我创建了一个简单的包装 class(至少比 System.IO.Packaging 更简单)returns 文件作为字节数组。显然这会消耗大量内存,我想改用流。

了解为什么调用 dispose 很重要,这样您就知道什么时候必须调用它,什么时候不需要。 Dispose and Finalize 密切相关。

Finalize 就是确保 GC 可以释放非托管资源(文件句柄、网络句柄、本地寻址内存 Space)。但是,虽然可以肯定 GC 运行,但它不确定 何时 它会这样做。事实上,如果它在应用程序关闭时只有 运行s,那是大多数实现的目标理想情况。

Dispose 就是要确保终结是确定性的。当您处置时,您不再完成。您尽早完成定稿工作。您可以在需要时执行,而不是在 GC 解决它时执行。

有很多细节,但对我来说归结为有两种情况你实现了 IDisposeable: * 您直接处理非托管资源。在这种情况下,您首先进行定稿。然后将 Dispose 作为 convenience/useability 特征。你不会真的 运行 进入这种情况,因为大多数都由框架程序员处理。 * 您处理任何 class 实现 IDisposeable 的唯一目的是 "relay" 对所有包含实例的 Dispose 调用。 95% 的 Disposeable 调用都与此有关。

对于 ZipArchive,我的猜测是它实现 IDisposeable 的主要原因,因为它必须包含文件句柄。 Entry 可能只是实现它,因为它持有对 ZipArchive 实例的引用。因此它应该转接电话。虽然 ZipArchiveEntry 中可能还有其他东西需要处理,但我认为这不太可能。因此,只要您确定 ZipArchive 实例已正确处理,您就应该能够取消使用。

简而言之,您正在走您无法走的捷径。正如您的问题标题已经从字面上说的那样,您正在尝试使用 依赖于 的 object 您处置的 object。

您要做的是打开一个文件,从中读取一些关于文件内部结构的信息(不是实际内部结构本身),然后 关闭文件,然后 然后 尝试使用该信息实际读取该文件。不可能; 文件已经关闭。 就像你不能从它自己的 using 块中 return 一次性 object 而不以处置结束因此无法使用 object,您显然也不能 return 依赖于 一次性 object 的东西。

所以,基本上,您的 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<> 作为参数。这实际上意味着上面代码中的 "Do stuff" 注释被替换为对您作为参数提供的任何函数的调用:

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<>,您也可以进行重载以提供 return 值。 <> 中的最后一个参数始终是 return 类型。但是你可以很容易地使用泛型来使它依赖于调用输入:

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);
        }
    }
}

您可以用相同的方式调用它,只是使用 return 是一个值而不是 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 object,甚至可能是源 ZipFile,这样你就可以用它做任何你想做的事情。

这种方法的唯一缺点是您实际上无法命名 Action<>Func<> 的组件,因此,在这种情况下,您无法仅从函数中知道UseMyArchiveStream 的签名是否给予 Func<Stream, String, T>String 参数将接收 entry.Nameentry.FullName。这同样适用于给出相同类型的多个参数;如果您在 Func<> 中有五个布尔选项,您可能很难准确记住哪个是哪个,而不必每次都查看代码。所以一定要在函数注释中准确记录,以免以后混淆。