C# 返回一次性,具体取决于父级一次性

C# 返回一次性,具体取决于父级一次性,c#,dispose,using,system.io.compression,C#,Dispose,Using,System.io.compression,以下是我正在编写的代码的一部分(为清晰起见进行了修改): 现在这失败了,因为归档文件是在getMyArchiveStream的出口处处理的,这会阻止myStream的使用 在处置myStream时,是否有办法处置存档? 另一种选择是保持归档打开,并使包含的类成为一次性的,但这在可用性方面有其自身的缺点 背景: 我创建了一个简单的打包类(至少比System.IO.packaging更简单),它以字节数组的形式返回文件。显然,这会消耗大量内存,我想改用流 了解调用dispose的原因很重要,这样您就

以下是我正在编写的代码的一部分(为清晰起见进行了修改):

现在这失败了,因为归档文件是在getMyArchiveStream的出口处处理的,这会阻止myStream的使用

在处置myStream时,是否有办法处置存档?

另一种选择是保持归档打开,并使包含的类成为一次性的,但这在可用性方面有其自身的缺点

背景:


我创建了一个简单的打包类(至少比System.IO.packaging更简单),它以字节数组的形式返回文件。显然,这会消耗大量内存,我想改用流

了解调用dispose的原因很重要,这样您就可以知道何时需要调用它,何时不需要调用它。它们之间有着密切的联系

Finalize是关于确保GC可以释放非托管资源(文件句柄、网络句柄、本机寻址内存空间)。但是,虽然可以确定GC将运行,但它在何时运行并不确定。事实上,如果它只在应用程序关闭时运行,这是大多数实现的理想情况

Dispose就是要确保最终结果是确定的。当您处理时,您不再完成。你提前完成定稿的工作。你可以在你想要的时候做,而不是等到GC来做的时候

有很多细节,但对我来说,可以归结为两种情况,即实现IDisposable: *您可以直接处理非托管资源。在这种情况下,您首先要完成定稿。然后作为方便/可用的功能进行处置。您不会真正遇到这种情况,因为大多数都是由框架程序员处理的。 *您处理实现IDisposeable的任何类,其唯一目的是将Dispose调用“中继”到所有关联实例。很容易,95%的可处理电话都是关于这一点的


对于ZipArchive,我想它实现IDisposable的主要原因是因为它必须包含文件句柄。而Entry可能只是实现它,因为它持有对ZipArchive实例的引用。因此,它应该中继呼叫。虽然ZipArchiveEntry可能还有其他东西需要处理,但我认为这不太可能。因此,只要确保ZipArchive实例得到了正确处理,您就应该能够使用它来减少这种情况。

简单地说,您正在使用无法使用的快捷方式。正如您的问题标题所说,您正试图使用一个依赖于您所处置的对象的对象

您要做的是打开一个文件,从中读取有关其内部的一些信息(而不是实际的内部),然后关闭该文件,然后尝试使用该信息实际读取该文件。这是不可能的;该文件已关闭。就像您无法使用块从其自身的
中返回一次性对象而不返回已处置且因此不可用的对象一样,您显然也无法返回依赖于一次性对象的内容

因此,基本上,您的
getMyArchiveStream
函数背后的整个思维过程都是有缺陷的。你根本不应该有这个功能。您只需将其他函数设置为如下所示:

public void UseMyArchiveStream()
{
    using(var archive = ZipFile.OpenRead(_filepath))
    {
        var entry = archive.GetEntry("test.path");
        using(var myStream = entry.Open())
        {
            //Do stuff
        }
    }
}
另一种选择是让
归档文件处于打开状态。。。但正如所评论的,还有另一种方法可以实现您想要的功能,那就是将
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);
        }
    }
}
使用
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);
        }
    }
}
请注意,您调用的函数不必与参数的确切签名匹配。通过这种调用方式,您可以用本地数据完美地替换缺少的参数

使用
布尔dostufwithfile(流文件、字符串入口名、布尔someOption、字符串输出文件夹)演示

只要需要由
UseMyArchiveStream
提供的输入正好是
=>
之前的部分,这将起作用。当然,你可以随心所欲地处理争论;您甚至可以将整个
ZipArchiveEntry
对象,甚至可能是源
ZipFile
赋予函数,这样您就可以对它执行任何操作


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

不要让
useMyArchiveStream
调用
getMyArchiveStream
,而是将
useMyArchiveStream
作为
Func
传递给
getMyArchiveStream
,这样第二次
使用
就发生在函数内部(而不是分开)第一个
使用
。或者,让
getMyArchiveStream
返回一个
元组
,让
useMyArchiveStream
处理
元组
的两个属性。这两个选项中的任何一个都有效吗@DerMaggi?我必须在下班后测试它们,但我更喜欢第一个想法每功能tha
UseMyArchiveStream(_filepath, "test.path", (str, nm) => SaveStreamToFile(str, nm));
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);
        }
    }
}
Boolean ok = UseMyArchiveStream(_filepath, "test.path", (str, nm) => DostuffWithFile(str, nm));
Boolean ok = UseMyArchiveStream(_filepath, "test.path", (str, nm) => DostuffWithFile(str, nm, true, _savePath));