.net - MemoryStream 而不是 byte[] 作为资源添加的文件?

标签 .net visual-studio-2010 stream resx

我有一个 .NET 程序集,我向其中添加了许多文件作为资源(二进制,每个 >500KB)。我以前一直使用 ResourceManager.GetObject() 访问这些文件。自动生成的方法 Resources类,返回 byte[] .

出于性能和语法原因,我宁愿将这些二进制资源作为流而不是字节数组进行操作。我发现,通过手动编辑 .resx 文件并更改 <value> 中的类的名称元素来自 System.Byte[]System.IO.MemoryStream ,我可以使用 ResourceManager.GetStream()以流的形式成功访问资源的方法,例如

<data name="MyFile" type="System.Resources.ResXFileRef, System.Windows.Forms">
    <value>..\Resources\MyFile.ext;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>

变成:
<data name="MyFile" type="System.Resources.ResXFileRef, System.Windows.Forms">
    <value>..\Resources\MyFile.ext;System.IO.MemoryStream, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>

这种方法的唯一缺点是 Visual Studio 总是在 byte[] 中添加新的文件资源。形式。有没有办法让它将类型设置为 MemoryStream为了我?

最佳答案

您可以在 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)返回 UnmanagedMemoryStream ,它实际上调用了 GetObject(String)在内部,仍然会创建一个副本。如果您调试应用程序并分析内存,您将看到内存仍在分配中。

我尝试了多种方法通过在 unsafe 中使用指针来解决这个问题。上下文和反射(reflection),但没有任何效果。 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 时它会根据资源的大小立即增加工作集。

您可能需要更换对 EndsWith 的调用。检查 list 中正确的资源名称,因为资源名称与直接通过 ResourceManager 访问它略有不同。 .

我实际上很失望,我无法使用现有的 ResourceManager 找到解决方案。 ,但它不够灵活。

编辑 我写了一个 in-depth blog article here about this subject .

关于.net - MemoryStream 而不是 byte[] 作为资源添加的文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10475593/

相关文章:

.net - 当需要依赖项时,如何/在何处将业务逻辑放入 EF 实体?

c# - 是否可以编写在 .NET 上运行的恶意软件?

c# - 域模型中的域服务与实体方法

c# - 有没有办法在设计时用 Visual Studio 2010 中的所有属性初始化一个对象?

c# - Resharper Active 热点禁用智能感知

c# - asp.net如何对GridView中的数据进行排序

html - Visual Studio 2010 powertools HTML 剪贴板支持

flash - 网络音频流

java - 有没有一种方法可以使用 Function< 将多个方法减少为一个方法? super T, ?> 作为方法参数?

C++ 二进制写入/读取 32 位到/从 64 位