用反射拦截方法

本文关键字:方法 反射 | 更新日期: 2023-09-27 18:01:36

我试图通过代理类拦截方法,并获得TargetException "对象不匹配目标类型"。我相信这类似于PostSharp这样的框架所做的,但我想看看我自己是否至少可以做一个练习。

本例中的目标是使用Diagnostics对方法调用进行计时。通过在一个新的委托中包装秒表。不过,我有点搞不懂了。

这是包装代理方法的委托:

    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是声明有问题的方法的实例,用[Time]属性装饰的实例。

谢谢。

用反射拦截方法

在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(interceptor,parameters)时,方法本身必须来自FunctionInterceptor类型。在这种情况下,它不是(你可以在那里创建它,但FunctionInterceptor不是新函数的声明类型)。实际上,新函数的声明类型将类似于()_<>ClassSomethingOrOther.