web-dev-qa-db-ja.com

リフレクションを使用してプライベートメソッドを呼び出すにはどうすればよいですか?

クラスにはプライベートメソッドのグループがあり、入力値に基づいて動的に呼び出す必要があります。呼び出しコードとターゲットメソッドの両方が同じインスタンスにあります。コードは次のようになります。

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });

この場合、GetMethod()はプライベートメソッドを返しません。プライベートメソッドを見つけるためにGetMethod()に提供する必要があるBindingFlagsは何ですか?

298
Jeromy Irvine

コードを変更して、オーバーロードされた GetMethodのバージョン を使用して、BindingFlagsを受け入れます。

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });

BindingFlags列挙ドキュメント です。

467
wprl

BindingFlags.NonPublicは、それ自体では結果を返しません。判明したように、それをBindingFlags.Instanceと組み合わせるとうまくいきません。

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);
65
Jeromy Irvine

そして、もしあなたが実際にトラブルに巻き込まれたいなら、拡張メソッドを書くことで実行しやすくします:

static class AccessExtensions
{
    public static object call(this object o, string methodName, params object[] args)
    {
        var mi = o.GetType ().GetMethod (methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance );
        if (mi != null) {
            return mi.Invoke (o, args);
        }
        return null;
    }
}

そして使用法:

    class Counter
    {
        public int count { get; private set; }
        void incr(int value) { count += value; }
    }

    [Test]
    public void making_questionable_life_choices()
    {
        Counter c = new Counter ();
        c.call ("incr", 2);             // "incr" is private !
        c.call ("incr", 3);
        Assert.AreEqual (5, c.count);
    }
50
cod3monk3y

Microsoft リフレクションAPIの変更 これらの回答のほとんどは廃止されました。以下は、最新のプラットフォーム(Xamarin.FormsおよびUWPを含む)で動作するはずです。

obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);

または、拡張方法として:

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
    var type = typeof(T);
    var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
    return method.Invoke(obj, args);
}

注意:

  • 目的のメソッドがobjのスーパークラスにある場合は、Tジェネリックをスーパークラスの型に明示的に設定する必要があります。

  • メソッドが非同期の場合は、await (Task) obj.InvokeMethod(…)を使用できます。

14
Owen James

これは継承ではできないと確信していますか?リフレクションは、問題を解決するときに最後に確認する必要があるものであり、リファクタリング、コードの理解、および自動化された分析をより困難にします。

DynMethodをオーバーライドするDrawItem1、DrawItem2などのクラスが必要なようです。

11
Bill K

特にプライベートメンバーに対する反省は間違っています

  • 反射は型安全性を破壊します。(もう)存在しないメソッド、間違ったパラメーター、またはパラメーターが多すぎるメソッドを呼び出すことができます。 、または十分ではありません...または間違った順序でさえ(これは私のお気に入り:))。ところで、戻り値の型も変更される可能性があります。
  • 反射が遅い

プライベートメンバーのリフレクションブレーク カプセル化 原則

  • クラスの内部動作を処理する必要があるため、コードの複雑さを増やします。隠されているものは隠されたままでなければなりません。
  • コードは簡単に壊れます。コンパイルされますが、メソッドの名前が変更された場合は実行されません。
  • プライベートコードが壊れやすいようにしますプライベートメソッドは、呼び出される前に何らかの内部状態を予期している場合があります。

とにかくそれをしなければならない場合はどうなりますか?

サードパーティに依存している場合や、公開されていないAPIが必要な場合は、いくつかの考察を行う必要があります。また、一部のクラスをテストするために使用しますが、テストのためだけに内部メンバーへのアクセスを許可するためにインターフェースを変更したくない場合もあります。

あなたがそれをするなら、それを正しくしなさい

  • 壊れやすいものを軽減します。

破損しやすい問題を軽減するには、継続的な統合ビルドなどで実行される単体テストでテストすることにより、潜在的な破損を検出することが最善です。もちろん、常に同じアセンブリ(プライベートメンバーを含む)を使用することを意味します。動的なロードとリフレクションを使用する場合、火で遊ぶのが好きですが、呼び出しが生成する例外をいつでもキャッチできます。

  • 反射の遅さを緩和します。

.Net Frameworkの最近のバージョンでは、CreateDelegateはMethodInfoの50倍の速度で起動します。

// The following should be done once since this does some reflection
var method = this.GetType().GetMethod("Draw_" + itemType, 
  BindingFlags.NonPublic | BindingFlags.Instance);

// Here we create a Func that targets the instance of type which has the 
// Draw_ItemType method
var draw = (Func<TInput, Output[]>)_method.CreateDelegate(
                 typeof(Func<TInput, TOutput[]>), this);

draw呼び出しは、MethodInfo.Invokeが標準のdrawとしてFuncを使用するよりも約50倍高速になります。

var res = draw(methodParams);

これを確認してください 私のポスト 異なるメソッド呼び出しのベンチマークを見る

6
Fab

描画するタイプごとに異なるDrawメソッドを用意することはできませんか?次に、オーバーロードされたDrawメソッドを呼び出して、描画するitemType型のオブジェクトを渡します。

あなたの質問は、itemTypeが異なるタイプのオブジェクトを本当に参照しているかどうかを明確にしません。

2
Peter Hession

渡すことができると思いますBindingFlags.NonPublicitGetMethodメソッドです。

1
Armin Ronacher

オブジェクトインスタンスの保護レベルにかかわらず、任意のメソッドを呼び出します。楽しい!

public static object InvokeMethod(object obj, string methodName, params object[] methodParams)
{
    var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
    var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
    MethodInfo method = null;
    var type = obj.GetType();
    while (method == null && type != null)
    {
        method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
        type = type.BaseType;
    }

    return method?.Invoke(obj, methodParams);
}
1

BindingFlags.NonPublic

0
Khoth

この(補足)答え(時々答え)を読んで、これがどこに行くのかを理解し、whyこのスレッドの一部の人は「まだ動作していません」

ここに答えの1つとまったく同じコードを書きました 。しかし、まだ問題がありました。ブレークポイントを配置しました

var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );

実行されましたが、mi == null

そして、関連するすべてのプロジェクトで「再構築」を行うまで、このような動作を続けました。反射法が3番目のアセンブリにある間、1つのアセンブリを単体テストしていました。それは完全に混乱しましたが、メソッドを発見するためにイミディエイトウィンドウを使用し、ユニットテストしようとしたプライベートメソッドの名前が古いことを発見しました(名前を変更しました)。これは、ユニットテストプロジェクトがビルドされても、古いアセンブリまたはPDBがまだ存在することを私に伝えました-何らかの理由で、テストされたプロジェクトはビルドされませんでした。 「再構築」が機能した

0
T.S.