有没有办法获取传递给方法的参数数组?

Posted

技术标签:

【中文标题】有没有办法获取传递给方法的参数数组?【英文标题】:Is there a way to get an array of the arguments passed to a method? 【发布时间】:2011-03-18 08:17:33 【问题描述】:

假设我有一个方法:

 public void SomeMethod(String p1, String p2, int p3)
 

 #if DEBUG
    object[] args = GetArguments();
    LogParamaters(args);
 #endif

     // Do Normal stuff in the method
 

有没有办法检索传递给方法的参数数组,以便记录它们?

我有大量的方法,并希望避免手动将参数按名称传递给记录器,因为人为错误将不可避免地蔓延。

我猜它会涉及某种形式的反射——这很好,因为它只会用于调试目的。

更新

更多信息:

我无法更改 SomeMethod 的方法签名,因为它公开为 WebMethod,并且必须复制它所模拟的遗留系统。

遗留系统已经记录了传入的参数。从新实现开始,将包装遗留系统,所以我希望记录进入 C# 版本的参数,以便我可以验证正确的参数以正确的顺序传入。

我只是想记录参数值和顺序,而不是它们的名称。

【问题讨论】:

日志中存储了哪些参数信息?类型、名称、值? 这意味着您实际上是在谈论参数,而不是参数。 (See e.g. here for an explanation about the difference.) @stax,是的,这种区别是有道理的。我正在寻找传递给方法的参数值。 【参考方案1】:

如果您使用Postsharp,您只需将属性添加到您要记录的方法。在此属性中,您可以编写日志记录代码,也可以提供您需要的参数。这就是所谓的横切关注点和 AOP(面向切面编程)

【讨论】:

+1。我打算写一些关于由编译器自动重写 CIL 输出以添加日志记录的内容,但仅使用已建立的产品可能会更容易。 同时执行上述操作也非常快速和容易。 代码示例?否则,它有点“仅链接”的答案..【参考方案2】:

我不确定访问调用堆栈的 API 是否提供了获取参数列表的方法。

但是有一些方法可以注入 IL 来拦截方法调用并执行自定义代码。

我经常使用的库是 Gael Fraiteur 的 PostSharp,它包含一个运行 postbuild 并根据您使用的 Aspects 在输出程序集中注入 IL 的应用程序。您可以使用属性来装饰程序集、类型或单个方法。例如:

[Serializable]
public sealed class LoggingAttribute : OnMethodBoundaryAspect

    public override void OnEntry(MethodExecutionArgs eventArgs)
    
        Console.WriteLine("Entering 0 1 2",
                          eventArgs.Method.ReflectedType.Name,
                          eventArgs.Method,
                          string.Join(", ", eventArgs.Arguments.ToArray()));

        eventArgs.MethodExecutionTag = DateTime.Now.Ticks;
    

    public override void OnExit(MethodExecutionArgs eventArgs)
    
        long elapsedTicks = DateTime.Now.Ticks - (long) eventArgs.MethodExecutionTag;
        TimeSpan ts = TimeSpan.FromTicks(elapsedTicks);

        Console.WriteLine("Leaving 0 1 after 2ms",
                          eventArgs.Method.ReflectedType.Name,
                          eventArgs.Method,
                          ts.TotalMilliseconds);
    

在这之后你可以用这个属性来装饰你想要的方法:

[Logging]
public void SomeMethod(String p1, String p2, int p3) 

   //..

【讨论】:

System.Diagnostics.StackTrace / System.Diagnostics.StackFrame 似乎只提供源文件名和行号信息等,而不是实际参数。)【参考方案3】:

好吧,如果你只是想传递值,你可以作弊并定义一个对象数组:

public static void LogParameters(params object[] vals)



这会导致值类型的装箱,但是也不会给你任何参数名称。

假设我有一个方法:

 public void SomeMethod(String p1, String p2, int p3) 
  

 #if DEBUG 
    LogParamaters(p1, p2, p3); 
 #endif 

     // Do Normal stuff in the method 
  

更新:很遗憾,反射不会自动为您完成所有工作。您需要提供这些值,但您可以使用反射来提供参数名称/类型:

How can you get the names of method parameters?

所以方法 sig 会变成这样的:

public static void LogParameters(string[] methodNames, params object[] vals)
 

然后您可以强制/假设每个集合中的每个索引都计数,这样methodNames[0] 的值就是vals[0]

【讨论】:

如果可能的话,我会尽量避免在 LogParameters 调用中写入参数名称,因此目标是:LogParamaters(GetMethodParams());其中 GetMethodParams() 将返回参数数组。我不太担心拳击开销。 我已经修改了我的答案,但正如其他人所说,有一些框架可用于附加必要的代码作为编译器步骤 - 这意味着您不需要手动编写很多代码。 当然,PostSharp 唯一的缺点是它不是免费的,你仍然需要编辑代码来放置属性。【参考方案4】:

params 帮助日志调用,但不会帮助现有的方法签名。使用AOP framework 进行日志记录可能是一种更高效的方法?

【讨论】:

【参考方案5】:

当然可以...查看这篇文章,它会获取参数的实际值。 how to enumerate passed method parameters

【讨论】:

【参考方案6】:

动态类型系统有一些功能可以做到这一点,但是你的类需要从动态基类继承

【讨论】:

你读过这个问题吗?他想记录进入函数的所有参数并希望访问该数据。【参考方案7】:

在某些情况下可能不起作用,但应该让你开始:)

class Program

    static void Main(string[] args)
    
        M1("test");
        M2("test", "test2");
        M3("test", "test2", 1);

        Console.ReadKey();
    

    static void M1(string p1)
    
        Log(MethodBase.GetCurrentMethod());
    

    static void M2(string p1, string p2)
    
        Log(MethodBase.GetCurrentMethod());
    

    static void M3(string p1, string p2, int p3)
    
        Log(MethodBase.GetCurrentMethod());
    

    static void Log(MethodBase method)
    
        Console.WriteLine("Method: 0", method.Name);
        foreach (ParameterInfo param in method.GetParameters())
        
            Console.WriteLine("ParameterName: 0, ParameterType: 1", param.Name, param.ParameterType.Name);
        
    

【讨论】:

在修复添加缺少的命名空间 System.Reflection 后,请注意它不会告诉值是什么。【参考方案8】:

只要您知道预期的类型,您就可以将它们记录在 SQL 数据库中。编写一个执行类型检查的方法,然后用参数(参数)值填充适当的 DB 列。如果您有自定义类型,则可以使用类型名称并将其作为字符串保存在它自己的特殊列中。

-编辑

另外,使用MethodBase.Name 扩展方法,您可以将您的参数与将它们作为参数的方法相关联,如下面的另一篇文章所述。成为跟踪使用的所有方法、使用哪些参数以及使用哪种类型的便捷方式。

这甚至是一个模糊的好主意吗? :)

【讨论】:

【参考方案9】:

这是我想出的解决方案:

PostSharp 或其他 AOP 解决方案在这种情况下并不实用,所以很遗憾我不得不放弃这个想法。

看起来虽然可以使用反射来参数名称和类型,但访问运行时值的唯一方法是附加调试器。

查看这里了解更多信息:

***

microsoft.public.dotnet.framework

所以这仍然给我留下了大约 50 个需要手动添加此日志记录的方法的问题。

反思救援...

public String GetMethodParameterArray()
    
        var output = new StringBuilder();
        output.AppendLine();

        Type t = typeof(API);
        foreach (var mi in t.GetMethods())
        
                var argsLine = new StringBuilder();
                bool isFirst = true;
                argsLine.Append("object[] args = ");
                var args = mi.GetParameters();

                foreach (var pi in args)
                
                    if (isFirst)
                    
                        isFirst = false;
                    
                    else
                    
                        argsLine.Append(", ");
                    
                    argsLine.AppendFormat("0", pi.Name);
                
                argsLine.AppendLine(";"); //close object[] initialiser

                output.AppendLine(argsLine.ToString());
                output.AppendFormat("Log(\"0\",args);", mi.Name);
                output.AppendLine();
                output.AppendLine();
            
        return output.ToString();
    

这段代码 sn-p 循环遍历类上的方法并输出一个 object[] 数组,该数组使用传递给方法的参数和一个包含参数和方法名称的 Log 调用进行初始化。

示例输出:

object[] args = username, password, name, startDate, endDate, cost;
Log("GetAwesomeData",args);

然后可以将此块粘贴到方法的顶部以达到所需的效果。

它比我想要的要手动得多,但它比手动输入参数要好得多,而且更不容易出错。

【讨论】:

以上是关于有没有办法获取传递给方法的参数数组?的主要内容,如果未能解决你的问题,请参考以下文章

获取传递给具有未知数量参数的方法的参数数量

有没有办法获取选项标签值并将其传递给车把中的助手?

如何从传递给某些 STL 算法的谓词中获取元素的索引?

有没有办法将字符串变量传递给 python .loc/.iloc?

JS传递对象数组为参数给后端,后端获取

spring MVC 怎么获取前端传递的数组参数