【问题标题】:How to get all parameters from an Action type variable如何从 Action 类型变量中获取所有参数
【发布时间】:2018-02-16 13:38:24
【问题描述】:

我需要构建一个可以记录在 Action 代码变量中传递的所有参数的跟踪。

考虑到action变量是用lambda表达式填充的,如何从Action实例中获取所有参数?

更新(明确动作对象的使用)

我在我正在尝试构建的 API 跟踪器下方发布。 API 跟踪器需要从 action 对象中传递的 lambda 表达式中获取参数:

public static class Tracer
{
    public static void log(Action action)
    {
      // note that action will always be set with a lambda function. See ServiceProxy class bellow.
      //..grab the action parameters and name and log them
    }
}

我只找到了从表达式中获取此类信息的方法。从动作类型对象中,我没有找到它是否包含参数,只找到了方法名称。

此 Tracer 用于公开服务的代理类。应记录所有呼叫。 Proxy 使用调用服务的函数包装器,如下所示:

class ServiceProxy:IServiceProxy {
   ...
   private void CallWrapper(Action func)
   {
        try
        {
            Tracer.log(func);
            func();
        }
        catch (Exception ex){...}
    }

    //method call example
    public void IServiceProxy.RequestX(String p1, int p2){
           CallWrapper(() =>  service.RequestX(p1, p2));//p1 and p2 parametersshould be logged by the Tracer.
    }       
}

为了重用CallWrapper 方法,lambda 表达式签名必须是动态的,这意味着不能将func 类型声明为泛型(例如:Action<String,Int>)。

【问题讨论】:

  • 哪些参数? Action 是无参数函数的委托类型。
  • IIRC 没有您可以从委托中获得的“参数”(因为它基本上只是“指向”方法的东西,并且实际上不包含任何更多信息,如参数等)。如果您想了解更多信息,那么您可能需要查看表达式树而不是使用委托 (see MSDN for Expression Trees)。
  • 如果您不调用它,您将无法找到它们......也许您想使用像Tracer.log(() => func(...))这样的表达式?
  • 我添加了 RequestX 方法来展示它是如何使用的以及应该记录什么。 Action 对象是一个 lambda 函数。
  • 我不明白反对意见。这是一个内容和形式上的有效问题。

标签: c# .net lambda


【解决方案1】:

您可以使用 Linq.Expressions 提取您的参数。
这意味着您需要将Action 更改为Expression<Action>
完成此操作后,您将能够使用自定义表达式访问者浏览您的 lambda。

所以让我们从添加一个新类开始检查你的表达式:

public class ArgumentExtractor : System.Linq.Expressions.ExpressionVisitor
{
    private Dictionary<string, object> arguments;
    public IDictionary<string, object> Arguments => arguments;

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        arguments = node.Arguments
            .Select(a =>
            {
                var memeber = a as MemberExpression;
                if (memeber == null)
                    return null;

                var name = memeber.Member.Name;
                var container = (memeber.Expression as ConstantExpression)?.Value;
                var value = container.GetType().GetField(name).GetValue(container);

                return new {name, value};
            })
            .Where(x => x != null)
            .ToDictionary(x => x.name, x => x.value);

        return base.VisitMethodCall(node);
    }
}

现在我们可以在您的Tracer 中使用它:

public static class Tracer
{
    public static void log(Expression<Action> action)
    {
        var argumentExtractor = new ArgumentExtractor();
        argumentExtractor.Visit(action);

        var arguments = argumentExtractor.Arguments;
        // to do: write it to log
    }
}

最后我们需要修改CallWrapper:

private static void CallWrapper(Expression<Action> func)
{
    Tracer.log(func);
    func.Compile().Invoke();
}

你可以找到完整的演示here

【讨论】:

    猜你喜欢
    • 2016-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 2017-05-17
    • 1970-01-01
    相关资源
    最近更新 更多