【问题标题】:Intercepting a method with reflection用反射拦截方法
【发布时间】:2011-07-06 23:06:09
【问题描述】:

我试图通过代理类拦截一个方法,并得到一个 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 实例来描述这里创建的新委托,这是基于 Method 是否具有指示它应该定时的某个属性:

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] 属性装饰的实例。

谢谢。

【问题讨论】:

  • 顺便说一句,PostSharp 通过修改 IL 并在执行前在方法中注入代码来做到这一点。
  • 是的,我也想多了,相似的结局不同的意思。

标签: c# reflection delegates proxy closures


【解决方案1】:

在您的 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。

【讨论】:

    猜你喜欢
    • 2016-06-28
    • 1970-01-01
    • 2014-10-11
    • 2013-05-23
    • 2011-04-16
    • 2012-04-01
    • 2012-08-18
    • 1970-01-01
    相关资源
    最近更新 更多