2011-07-06 73 views
4

我試圖通過代理類攔截一個方法,並得到一個TargetException「對象與目標類型不匹配」。我相信這與PostSharp這樣的框架類似,但我想看看我是否可以自己作爲一個練習來做到這一點。用反射截取一個方法

本例中的目標是使用Diagnostics.Stopwatch將方法調用包裝到一個新代理中。儘管如此,它還是讓我有點頭痛。

這裏是一個包裝了代理的方式委託:

public static Func<Object> Time(this MethodInfo target, object parent, object[] parameters, Action<string> logger) 
    { 
     return delegate 
      { 
       Stopwatch s = new Stopwatch(); 

       s.Start(); 
       object value = target.Invoke(parent, parameters); 
       s.Stop(); 

       logger("Elapsed ms for function '" + target.Name + "' = " + s.ElapsedMilliseconds.ToString()); 

       return value; 
      }; 
    } 

然後在這裏是做攔截,實質上產生描述在這裏創造了新的委託,這是基於一個新的MethodInfo實例的方法該方法是否具有一定的屬性,指示它應定時:

public class FunctionInterceptor 
{ 
    public MethodInfo Intercept(Object proxy, MethodInfo method, Object[] args) 
    { 
     return new Func<Object>(() => 
     { 
      var data = method.GetCustomAttributes(typeof(TimeAttribute), true); 

      object result = default(object); 

      foreach (object d in data) 
      { 
       if (d.GetType() == typeof(TimeAttribute)) // [Time] attribute 
       { 
        result = method.Time(proxy, args, Log.Write).DynamicInvoke(args); 
       } 
      } 
      return result; 

     }).Method; // returning MethodInfo of this delegate 
    } 

現在看來,我應該能夠調用的委託,這MethodInfo對象描述:

var interceptor = new FunctionInterceptor(); 

retVal = interceptor.Intercept(proxy, method, parameters).Invoke(interceptor, parameters); 

但我得到錯誤 - 對象不符合目標類型。我檢查了MethodInfo實例,並且DeclaringType是FunctionInterceptor,這意味着我應該像上面那樣傳入攔截器的實例。不確定是什麼問題。

如果我這樣做,它工作正常(只需調用的MethodInfo不其包裝):

retVal = method.Invoke(obj, parameters); 

如果obj是實例聲明有問題的方法中,一個飾以[時間]屬性。

謝謝。

+1

順便說一句,PostSharp通過修改IL並在執行前在方法內部注入代碼來實現它。 –

+0

是的,我認爲很多,類似的結局不同的手段。 –

回答

3

在您的Intercept調用中,您正在創建一個新的MethodInfo對象。該MethodInfo對象與您傳入的對象完全不同。它不是來自繼承自原始對象類型的類(也不是來自FunctionInterceptor類)。如果你不是做了這樣的事情:

public object Intercept(object proxy, MethodInfo method, object[] args) 
{ 
    var data = method.GetCustomAttributes(typeof(TimeAttribute), true); 

    object result = default(object); 

    foreach (object d in data) 
    { 
     if (d.GetType() == typeof(TimeAttribute)) // [Time] attribute 
     { 
      result = method.Time(proxy, args, Log.Write).DynamicInvoke(args); 
     } 
    } 
    return result; 
} 

它會工作,因爲在這種情況下,方法實際上來自代理的類型。當你調用Invoke(攔截器,參數)時,方法本身必須來自FunctionInterceptor類型。在這種情況下,它不是(您可以在那裏創建它,但FunctionInterceptor不是新函數的聲明類型)。實際上,新函數的聲明類型將是類似於()的ClassSomethingOr或其他類。