2008年3月12日水曜日

[.NET]Reflectionでprivateフィールドに値を突っ込む

.NET Frameworkのクラスや、提供されているライブラリを使った単体テストをしようとした場合、時々、privateなフィールドを強制的に変更したい場合があります。そんなときにReflectionを使います。
コードはこんな感じ。

//呼び出し元
HogeEx he = new HogeEx();
Type tp = typeof(Hoge);
FieldInfo info = tp.GetField("_bar"BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance);
info.SetValue(hetrue);

internal class Hoge
{
    // Fields
    public bool _foo = true;
    private bool _bar = false;
}

internal class HogeEx : Hoge
{
    // Fields
    public bool _fooEx = true;
    private bool _barEx = false;
}


ポイントとしては、

  1. 実際にprivateフィールドを持っているクラスのタイプを指定する

  2. Type.GetField()の第二引数に、BindingFlags.Instance|BindingFlags.NonPublicを指定する

  3. 値を突っ込む場合、↑に加え、BindingFlags.SetFieldを指定する

です。

たとえば、HttpRequestのFormに対して強引に値を突っ込みたい場合を考えてみます。HttpRequestのプロパティForm(NameValueCollectionの派生クラス)は参照のみの仕様です。値をセットしようとすると例外が発生します。これは、Form(NameValueCollection)のベースクラスであるNameObjectCollectionBaseのReadOnlyプロパティにより制御しています。そのプロパティの元はprivate変数の_readOnlyとなります。これにfalseを設定すればよい。ということになります。



HttpRequest req = new HttpRequest("""http://localhost/"""); //適当なURLが指定できないのでlocalhostを指定
Type tp = typeof(NameObjectCollectionBase);
FieldInfo info = tp.GetField("_readOnly"BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance);
info.SetValue(req.Formfalse);
req.Form.Add("foo""bar");

0 件のコメント: