您可以在ResourceManager
.
public static class ResourceExtensions
{
public static MemoryStream GetMemoryStream(this ResourceManager resourceManager, String name) {
object resource = resourceManager.GetObject(name);
if (resource is byte[]) {
return new MemoryStream((byte[])resource);
}
else {
throw new System.InvalidCastException("The specified resource is not a binary resource.");
}
}
}
打电话
ResourceManager resourceManager = Properties.Resources.ResourceManager;
MemoryStream stream = resourceManager.GetMemoryStream("binaryResource");
不过,这似乎也一样。
MemoryStream stream = new MemoryStream(Properties.Resources.SomeBinaryResource);
我不确定我是否会修改资源文件,因为它们很容易更改,而且我确信在某些情况下 Visual Studio 会覆盖更改。
根据您的担忧,问题在于这会将数据的副本创建到内存中,从而产生内存占用。对于寿命很短的轻量级资源,这不是问题,但它可能是一个大问题。
答案很简短:您无法避免使用ResourceManager
. 问题是两者都ResourceManager.GetObject(String)
创建ResourceManager.GetStream(String)
了数据的副本。即使GetStream(String)
返回 an UnmanagedMemoryStream
,它实际上也会在GetObject(String)
内部调用,并且仍然会创建一个副本。如果您调试应用程序并分析内存,您将看到内存仍然被分配。
我尝试了多种方法来解决这个问题,方法是在unsafe
上下文中使用指针和反射,但没有任何效果。ResourceManager
只是没有那么灵活或优化。
但是,我能够找到解决方案,但它需要您使用Embedded Resources
. 除了将资源文件的构建操作设置Embedded Resource
为Build Action
. 使用它,您可以使用反射来创建一个UnmanagedMemoryStream
不创建数据副本的对象。
private UnmanagedMemoryStream GetUnmanagedMemoryStream(String embeddedResourceName) {
Assembly assembly = Assembly.GetExecutingAssembly();
string[] resourceNames = assembly.GetManifestResourceNames();
string resourceName = resourceNames.SingleOrDefault(resource => resource.EndsWith(embeddedResourceName, StringComparison.InvariantCultureIgnoreCase));
if (resourceName != null) {
return (UnmanagedMemoryStream)assembly.GetManifestResourceStream(resourceName);
}
else {
throw new System.ArgumentException("The specified embedded resource could not be found.", "embeddedResourceName");
}
}
我没有对此进行广泛的测试,但它确实有效。我的测试数据是一个 17 兆字节的小文件。我的测试应用程序的工作集内存从大约 50 兆字节开始,在将资源检索到流中之后,它不会改变。当使用ResourceManager
它时,它会立即通过资源的大小增加工作集。
You will probably need to swap out the call to EndsWith
that checks for the proper resource name in the manifest, because the names of the resources are slightly different than accessing it directly through the ResourceManager
.
I am actually disappointed that I was not able to find a solution using the existing ResourceManager
, but it just isn't flexible enough.
Edit I wrote an in-depth blog article here about this subject.