10

.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>

このアプローチの唯一の欠点は、VisualStudioが常にbyte[]フォームに新しいファイルリソースを追加することです。タイプを設定する方法はありMemoryStreamますか?

4

1 に答える 1

7

で拡張メソッドを作成できます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);

リソースファイルは変更が難しいため、変更するかどうかはわかりません。また、VisualStudioが変更を上書きするシナリオがあると確信しています。

これに関する問題は、あなたの懸念によると、これがデータのコピーをメモリに作成し、メモリフットプリントを作成することです。短命である軽量リソースの場合、それは問題ではありませんが、それは大きな懸念事項になる可能性があります。

答えは短いです。を使用してメモリフットプリントを回避することはできませんResourceManager。問題は、両方ResourceManager.GetObject(String)ResourceManager.GetStream(String)データのコピーを作成することです。GetStream(String)を返しますが、UnmanagedMemoryStream実際にはGetObject(String)内部を呼び出し、コピーは作成されたままです。アプリケーションをデバッグしてメモリのプロファイルを作成すると、メモリがまだ割り当てられていることがわかります。

コンテキストでポインターを使用してこれを回避するための複数の方法を試しましたがunsafe、リフレクションでは何も機能しませんでした。ResourceManagerそれほど柔軟でも最適化されていません。

解決策を見つけることができましたが、を使用する必要がありますEmbedded ResourcesEmbedded 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リソースの名前は、を介して直接アクセスする場合とは少し異なるため、マニフェストで適切なリソース名をチェックする呼び出しを交換する必要がありResourceManagerます。

私は実際に既存のものを使用して解決策を見つけることができなかったことに失望していますResourceManagerが、それは単に十分な柔軟性がありません。

編集私はこの主題についてここに詳細なブログ記事を書きました。

于 2012-05-07T01:20:46.520 に答える