web-dev-qa-db-ja.com

C#で簡単な動的プロキシを作成する方法

オブジェクトに特定の機能を追加するために、動的なプロキシオブジェクトを作成します。

基本的には、オブジェクトを受け取り、取得した元のオブジェクトと同一のオブジェクトでラップし、すべての呼び出しをインターセプトします。

class Wrapper : DynamicProxy// dynamic proxy is not a reall class, but i guess something like this exists...
{
    public static T Wrap(T obj)
    {
        return (T) new Wrapper(obj);
    }

    public override object InterceptCall(MethodInfo info, object[] args)
    {
        // do stuff
    }

}

明確にするために、WCFチャネルファクトリに似た何かをしたい...


クラス(プロキシではない)をプロキシし、非仮想メソッドを処理する(「new」キーワードでmethondを継承して追加したかのように)良い方法が必要なので、賞金を追加します。 .Netが行うように、これらはすべて非常に可能性が高いと確信しています。

49
AK_

私はこれをもっと早く書くべきでしたが、気にしません。

私の問題には、インターフェイスではなくクラスをプロキシするために必要な特別な「落とし穴」がありました。

これには2つの解決策があります。

  1. Real Proxy and friends、基本的には.Net Remotingを使用することを意味します。 ContextBoundObjectから継承する必要があります。

  2. System.Reflection.Emit を使用してプロキシを構築する spring と同様に、 ProxyFactoryObject のコードも参照できます。 別の 3 記事subject にあります。

ところで、2番目のアプローチにはかなりの制限があり、非仮想メソッドをプロキシすることはできません。

14
AK_

DynamicObjectImpromptuInterface の組み合わせでこれを行うことができますが、プロキシする機能とプロパティを実装するインターフェイスが必要です。

public interface IDoStuff
{
    void Foo();
}

public class Wrapper<T> : DynamicObject
{
    private readonly T _wrappedObject;

    public static T1 Wrap<T1>(T obj) where T1 : class
    {
        if (!typeof(T1).IsInterface)
            throw new ArgumentException("T1 must be an Interface");

        return new Wrapper<T>(obj).ActLike<T1>();
    }

    //you can make the contructor private so you are forced to use the Wrap method.
    private Wrapper(T obj)
    {
        _wrappedObject = obj;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        try
        {
            //do stuff here

            //call _wrappedObject object
            result = _wrappedObject.GetType().GetMethod(binder.Name).Invoke(_wrappedObject, args);
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }
}

もちろん、タイプセーフを失い、私が示したようにDynamicObjectを使用して、ダックキャスティングを削除することもできます。

このオブジェクトプロキシの透明な拡張可能なバージョンを作成し、それをオープンソース化しました here

36
albertjan

Castle.DynamicProxy に加えて、 LinFu.DynamicProxy on Github もあります。

14

PostSharp を見てください。 Vanilla .Netであなたが望むことをする方法はわかりませんが、PostSharpは、メソッド内でコードを置換またはラップするために使用できる "OnMethodBoundaryAspect"などを提供します。

ロギング、パラメーター検証、例外処理などの処理に使用しました。

無料のCommunity Editionがあります。開発マシンと、使用するビルドサーバーにインストールする必要があります。

5
NeilD

別のオプションは ContextBoundObject です。

8〜9年前に、このアプローチを使用してメソッド呼び出しをトレースするCodeProjectに関する記事がありました。

2
leppie

クラス内のすべての関数の前後に機能を追加するには、リアルプロキシが適切なアプローチです。

そのため、Tには任意のTestClassを使用できます。 TestClass-に対してこのようなインスタンスを作成します

var _instance =(object)DynamicProxy(TestClass).GetTransparentProxy();

ダイナミックプロキシのコード

 class DynamicProxy<T> : RealProxy
    {
        readonly T decorated;

        public DynamicProxy(T decorated) : base(typeof(T))
        {
            this.decorated = decorated;
        }

        public override iMessage Invoke(iMessage msg)
        {
            var methodCall = msg as IMethodCallMessage;
            var methodInfo = methodCall.MethodBase as MethodInfo;
            string fullMethodName = $"{methodInfo.DeclaringType.Name}.{methodCall.MethodName}";

            try
            {
                var result = methodInfo.Invoke(decorated, methodCall.InArgs);

                return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
            }

            catch (Exception e)
            {
                return new ReturnMessage(e, methodCall);
            }
            finally
            {
            }
        }
    }
0
Ankita Sachan