获取有关Func <T>委托的调用方和参数名称和值的信息

问题描述

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

  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"}));
      }
        

解决方法

在所提供的情况下,您可以使用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",new [] {"x","z"},1"
        return MetodoDaEseguire.Compile()();    
    }
}

然后呼叫ExternalMethod();将显示:

ExternalMethod
par1,JobID
"a",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()();

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

,

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

            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();
            }

enter image description here