2011-12-05 23 views
47

我想構建一個動態代理對象以將某些功能添加到對象。如何在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通道工廠的東西...


我添加一個賞金,因爲我需要代理的好方法類(而不是接口)並處理非虛擬方法(就像我在「新」關鍵字下繼承和添加了一個方法)。 我確信這一切都是非常可能的,因爲.Net可以。

+4

的代碼你看着http://www.castleproject.org/dynamicproxy/index.html? –

+0

@ np-hard如果你發佈這個答案,我會接受它...不是100%我在找什麼,但足夠好。 –

+0

您是否知道在編譯時或者在運行時需要代理的類型? – Sneal

回答

12

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

我的問題有一個特殊的「陷阱」,我需要能夠代理類而不是接口。

有兩個解決辦法:

  1. Real Proxy和朋友,基本上意味着使用.NET遠程。需要一個從ContextBoundObject繼承。

  2. 使用System.Reflection.Emit建立一個代理,如spring所做,你也可以看看他們的代碼ProxyFactoryObject。這裏有another三個articles,subject

順便說一下,第二種方法有相當的限制,你不能代理非虛擬方法。

+0

你可以添加一個如何實現這一目標的代碼示例?非常基本的東西... – Tsury

+0

不,但你可以按照鏈接找到更好的例子和補充,比我可以寫在這裏的任何東西。 –

+0

您的'http:// dcooney.com /'鏈接已損壞。你有替代品嗎? – Basic

5

的AOP框架看看PostSharp。 我不知道在vanilla.Net中做什麼的方法,但是PostSharp提供了諸如「OnMethodBoundaryAspect」之類的東西,可以用來替換或者包裝方法中的代碼。

我用它來做一些像日誌,參數驗證,異常處理等

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

+0

Postharp在編譯時可用於已知類型。我不認爲是OP所需要的 – sam

33

你可以用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; 
     } 
    } 
} 

你可能會選擇失去類型安全性,並像我展示的那樣使用動態對象,然後放下鴨子。

我做了這個對象代理的transparant可擴展版本,並打開它here

+0

你能說一下關於ImpromptuInterface如何在引擎蓋下工作嗎?也就是說,'ActLike'使用什麼技術來創建任何類型的對象?某種字節碼生成? – Lii

+0

@Li我沒有開發Impromptu接口。但它使用DLR動態綁定到鴨子對象上的函數。然後它使用相同的技巧將其轉換爲您指定的接口。 – albertjan

+0

像這樣的性能影響是什麼? AFAIK'.GetType()'不是很好的性能。 – FrankerZ

2

另一種選擇是ContextBoundObject

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

0

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

所以現在在T中可以是任何TestClass。創建像TestClass這樣的實例 -

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

動態的Proxy-

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 
      { 
      } 
     } 
    } 
相關問題