【问题标题】:Get info about caller and parameters (name and value) of a Func<T> delegate获取有关 Func<T> 委托的调用者和参数(名称和值)的信息
【发布时间】:2021-01-02 00:44:38
【问题描述】:

我有一个类公开一个接受 Func 委托并异步运行它的公共方法 (ExecuteJob)。我需要了解以下信息:

  1. 调用者(调用 ExecuteJob 的类和方法名)
  2. 用于调用 Func 委托的参数列表及其值
  3. 如果可能,在运行 Func 委托之前,设置此委托的特定参数的值

示例代码

    public static class JobHandlerManager
          {
            public static async Task ExecuteJob<T>(Func<T> MetodoDaEseguire)
            {
              Params parOfMethod = MetodoDaEseguire.GetMethodParams();                  
              // here I need to get info about above points
              string Caller = "ExternalMethod"
              string Parameters = "\"par1\" = \"a\", \"par2\" = 10, \"par3\" = [\"x\", \"y\", \"z\"] "
              int JobID = CalculateJobId();
              parOfMethod = parOfMetho.add(JobID);
              Func<T> NewMethod = MetodoDaEseguire(parOfMethod);
              Task.Run(MetodoDaEseguire);
            }
          }

     

 public void DoStuff(string par1, int par 2, string[] arraypar, int JobID)
      {
        // JobID is set by JobHandlerManager.ExecuteJob
      }
      public void ExternalMethod()
      {
        JobHandlerManager.ExecuteJob(() => DoStuff("a", 10, new string[] {"x", "y", "z"}));
      }
        

【问题讨论】:

    标签: c# delegates


    【解决方案1】:

    我自己从上一个问题中找到了一个解决方案,包括 ma 静态类中的这两个方法。

                private static object GetArgumentValue(Expression element)
                {
                    if (element is ConstantExpression)
                    {
                        return (element as ConstantExpression).Value;
                    }
        
                    var l = Expression.Lambda(Expression.Convert(element, element.Type));
                    return l.Compile().DynamicInvoke();
                }
        
                private static string[] GetParamArgumentsValue(ReadOnlyCollection<Expression> Arguments)
                {
                    List<string> ParamArgumentsValue = new List<string>();
                    foreach (var arg in Arguments)
                    {
                        ParamArgumentsValue.Add(JsonConvert.SerializeObject(GetArgumentValue(arg)));
                    }
                    return ParamArgumentsValue.ToArray();
                }
    

    【讨论】:

      【解决方案2】:

      在提供的情况下,您至少可以使用expression treesCallerMemberNameAttribute 实现部分功能:

      public static int DoStuff(string par1, int par2, string[] arraypar, int JobID)
      {
          // JobID is set by JobHandlerManager.ExecuteJob
          return 1;
      }
      public static void ExternalMethod()
      {
          JobHandlerManager.ExecuteJob(() => DoStuff("a", 10, new string[] { "x", "y", "z" },1));
      }
      
      public static class JobHandlerManager
      {
          public static T ExecuteJob<T>(Expression<Func<T>> MetodoDaEseguire, [CallerMemberName]string caller = "")
          {
              Console.WriteLine(caller); // will print "ExternalMethod"
              var mc = MetodoDaEseguire.Body as MethodCallExpression;
              Console.WriteLine(string.Join(", ", mc.Method.GetParameters().Select(p => p.Name))); // will print "par1, par2, arraypar, JobID"
              Console.WriteLine(string.Join(", ", mc.Arguments)); // will print ""a", 10, new [] {"x", "y", "z"}, 1"
              return MetodoDaEseguire.Compile()();    
          }
      }
      

      调用ExternalMethod(); 将打印:

      ExternalMethod
      par1, par2, arraypar, JobID
      "a", 10, new [] {"x", "y", "z"}, 1
      

      但要注意性能和闭包(即获取从局部变量、字段等传递的实际值。需要做更多的工作)。

      要获取调用者类,您可以尝试查看 Environment.StackTraceCallerFilePathAttribute 例如(取决于您的需要)。

      UPD

      要替换某些参数,您可以执行以下操作:

      var newArgs = mc.Arguments.ToList();
      var ardIdToReplace = newArgs.Count - 1; // find somehow index of replaced argument
      newArgs[ardIdToReplace] = Expression.Constant(42);
      var newLambda = Expression.Lambda<Func<T>>(Expression.Call(mc.Method, newArgs));
      return newLambda.Compile()();
      

      请记住,由于表达式的自定义处理,这也很容易出错,因为您会失去很多编译时安全性(用户可以传递很多比我的解决方案可以支持的更多)。

      【讨论】:

      • 在您的解决方案中这是不可能的:1)使用默认参数值调用方法。 2) 在调用 Func Delegate 之前编辑 JobID 值
      • @DanieleFrisenna 2) 您可以操作表达式树,包括替换一些参数。
      • 您能帮我了解如何在调用方法之前编辑值吗?
      • 我按照您在 UPD 中提到的那样修改了我的代码,如果我调用它就不起作用:ExecuteJob 传递调用者类中定义的方法(例如:JobHandlerManager.ExecuteJob(() => DoStuff("a" , 10, new string[] {"x", "y", "z"}));) 但是如果我使用静态方法可以完美地工作
      • @DanieleFrisenna 尝试更改为 Expression.Call(mc.Object, mc.Method, newArgs)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      相关资源
      最近更新 更多