簡體   English   中英

如何在C#中做一個簡單的動態代理

[英]How to make a simple dynamic proxy in C#

我想構建一個動態代理 object 來為 object 添加某些功能。

基本上我想收到一個 object,用一個看起來與我得到的原始相同的 object 包裝它,並攔截所有呼叫。

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”關鍵字下繼承並添加了一個方法一樣)。 我確信這一切都是非常有可能的,因為.Net 做到了。

您可以使用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 ,然后放棄鴨子鑄造。

我制作了這個對象代理的透明可擴展版本,並在此處開源。

我應該早點寫這個,但沒關系。

我的問題有一個特殊的“問題”,我需要能夠代理類而不是接口。

對此有兩種解決方案:

  1. RealProxy和朋友們,基本上意味着使用 .NET Remoting。 需要一個從ContextBoundObject繼承。

  2. spring一樣使用System.Reflection.Emit構建代理,您還可以查看其ProxyFactoryObject的代碼。 這里是關於這個主題的另外三篇文章

    • 這種方法的主要缺點是限制您只能覆蓋virtual成員。

看看PostSharp 我不知道有什么方法可以在 vanilla .Net 中執行您想要的操作,但是 PostSharp 提供了諸如“OnMethodBoundaryAspect”之類的東西,可用於替換或包裝方法中的代碼。

我用它來做日志、參數驗證、異常處理等事情。

有一個免費的社區版,應該適合您。 您需要將它安裝在您的開發機器以及您使用的任何構建服務器上。

另一種選擇是ContextBoundObject

大約 8-9 年前,有一篇關於 CodeProject 的文章使用這種方法來跟蹤方法調用。

為了在類中的每個函數之前和之后添加任何功能,Real proxy 是一個很好的方法。

所以現在在 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
            {
            }
        }
    }

除非您讓 CLR 掛鈎到對該對象的每個方法/屬性調用並將調用重定向到您創建的虛假成員,否則您無法攔截對靜態成員、非虛擬成員或私有成員的所有調用。 您可以通過使用.NET Profiler API來實現這一點。 例如,TypeMock Isolator 使用它來監視應用程序的執行,並且在調用方法時,CLR 會通知 typemock 隔離器,這允許 Isolator 完全覆蓋原始類。

.NET 6.0 向Reflection命名空間添加了一個新的候選對象: DispatchProxy 團隊在這里宣布。 文章中包含一個示例用法。

您可以僅使用 System.Danymic 命名空間中的 DynamicObject 來執行此操作,而無需使用任何第三方庫。

示例代碼:

public class DynamicProxy: DynamicObject
    {
        private readonly T _object;

        // The inner dictionary.
        Dictionary<string, object> dictionary = new Dictionary<string, object>();

        // Getting a property.
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return dictionary.TryGetValue(binder.Name, out result);
        }

        // Setting a property.
        // You can set up access control eg. if you don't want to 
        // set certain field, you can return false before putting
        // the value into the inner dictionary
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            if (binder.Name.Equals("Rating")) return false;
            dictionary[binder.Name] = value;
            return true;
        }

        public DynamicProxy(T object)
        {
            _object = object;
            dictionary["Name"] = object.GetName();
            dictionary["Gender"] = object.GetGender();
            dictionary["Interests"] = object.GetInterests();
            dictionary["Rating"] = object.GetGeekRating();
        }

        public string GetName()
        {
            return (string)dictionary["Name"];
        }

        public int GetGeekRating()
        {
            return (int)dictionary["Rating"];
        }
}

然后在驅動 class 上:

dynamic dynamicProxy = new DynamicProxy(person);

這樣,您可以設置和獲取具有訪問控制的字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM