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.Name
或 entry.FullName
。这同样适用于给出相同类型的多个参数;如果您在 Func<>
中有五个布尔选项,您可能很难准确记住哪个是哪个,而不必每次都查看代码。所以一定要在函数注释中准确记录,以免以后混淆。
这是我正在处理的部分代码(为清楚起见进行了修改):
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.Name
或 entry.FullName
。这同样适用于给出相同类型的多个参数;如果您在 Func<>
中有五个布尔选项,您可能很难准确记住哪个是哪个,而不必每次都查看代码。所以一定要在函数注释中准确记录,以免以后混淆。