キー文字列と StronglyTypedResourceBuilder

埋め込みリソースへのアクセスにはストリーム名が必要だし、マネージドリソースからオブジェクトを取り出すにもキー文字列が必要であるにもかかわらず、データバインディングやリフレクション同様に、.NET ではソースコード上に現れる識別子としてのキー文字列をうまく取り扱うことができない。このあたりは難読化などの問題とあいまって .NET アプリケーション構築の悩みの種となる。
Borland Delphi では string とは別に resourcestring という文字列定数型があり、ソースコード中に、

var ApplicationName: resourcestring = 'RESX ファイルソートツール';

のように文字列定数を宣言しておくことで自動的に文字列テーブルに ApplicationName というキーと対応する文字列が登録される機能がある。*1
これを目指して

  public class ResourceString
  {
    static ResourceString()
    {
      ResourceManager rm = new ResourceManager(typeof(ResourceString));
      foreach (FieldInfo fi in typeof(ResourceString).GetFields(BindingFlags.Static))
      {
        fi.SetValue(null, rm.GetString(fi.Name));
      }
    }

    public readonly string ApplicationName;
    public readonly string GreetingMessage;
    // :
  }

なんてもので適当にごまかしたりもした。*2
.NET 2.0 では System.Resources.Tools.StronglyTypedResourceBuilder というクラスがあり、resgen.exe の /str オプションでこのクラスのもつ機能を呼び出すことができる。これは .resx から .resources を生成するかわりに、.resx に対してタイプセーフな実装テンプレートを自動生成してくれる機能であり、CodeDOM 対応のすべての .NET 対応言語で利用できる。*3
生成されるソースコードは、

  public class XXX
  {
    protected static ResourceManager rm = new ResourceManager(typeof(XXX));

    public static ResourceManager { get { return this.rm; }}

    public static string ApplicationName { get { return this.rm.GetString("ApplicationName"); }}
    public static string GreetingMessage { get { return this.rm.GetString("GreetingMessage"); }}
    public static Bitmap SplashImage { get { return (Bitmao) this.rm.GetObject("SplashImage"); }}
  }

という感じで、ResourceManager から取得できる要素を適切な型で公開する static property が並ぶもので、あまり期待できたものではない。DotFuscator などの難読化ツールでは、これらの情報をどう扱うのか興味がある*4のだが、たとえば DotFuscator Professinal は 30 万円もする*5わけで、個人では当然ながら仕事でも購入するには数歩引いてしまう価格である。

*1:当然、各言語別のリソースファイルを用意するだけで、この文字列を対応言語のものに入れ替えることができる。

*2:カルチャが動的に切り替わることなどを考えてないので、こんなことしないように

*3:つまり、標準では C#VB のみで、C++ は NG

*4:文字列リテラルを簡易暗号化したり、埋め込みリソースをストリームまるごと暗号化するぐらいはできるようだが

*5:フリーのツールもあるし、他社からより安価なものもあるし、英語版を$で買えば半額程度だったりもする