2010-07-20 229 views
22

说我有一个方法:有没有办法获得传递给方法的参数数组?

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#版本,以便我可以验证正确的参数以正确的顺序传入。

我只是在寻找记录参数值和顺序,而不是他们的名字。

+0

有关参数的信息存储在日志中?类型,名称,价值? – devnull 2010-07-20 09:36:55

+0

这意味着你真的在讨论_arguments_,而不是_parameters_。 [(例如,参见这里的关于差异的解释。)](http://msdn.microsoft.com/en-us/library/9kewt1b3%28VS.80%29.aspx) – stakx 2010-07-20 10:00:31

+0

@stax,是的区别是有意义的。我正在寻找传递给方法的参数值。 – 2010-07-20 10:05:35

回答

0

这就是我想出了一个解决方案:

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

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

这里看到更多的信息:

StackOverflow

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

此代码段遍历一个类的方法和输出对象[]数组传递到方法和含有该参数的日志调用的参数初始化和方法名称。

输出示例:

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

该块然后可以被粘贴到达到所要求的效果的方法的顶部。

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

0

有与动态类型系统,可以做到这一些功能,但随后你的类需要从动态基类

继承
+0

你甚至读过这个问题吗?他想记录所有进入函数的参数并想访问这些数据。 – Oded 2010-07-20 09:28:56

1

好吧,如果你只是想传递的价值观,你可以欺骗和定义对象数组:

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?

因此该方法的签名会更改为类似:

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

然后你就可以强制/假设每个集合中的每个索引都符合,因此methodNames[0]的值为vals[0]

+0

如果可能的话,我试图避免在LogParameters调用中写入参数名称,因此需要执行以下操作: LogParamaters(GetMethodParams()); GetMethodParams()将返回参数数组。 我并不太在意拳击的开销。 – 2010-07-20 09:33:38

+0

我修改了我的答案,但正如其他人所说,有框架可用于将必要的代码作为编译器步骤添加 - 意味着您不需要手动编写大量代码。 – 2010-07-20 09:37:43

+0

当然,PostSharp唯一的缺点是它不是免费的,你仍然需要编辑代码来放置属性。 – 2010-07-20 09:42:16

7

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

+0

+1。我打算写一些关于自动重写编译器的CIL输出以添加日志的方法,但只使用已建立的产品可能会更容易。 – stakx 2010-07-20 10:04:13

+0

同样做到以上是快速和容易做到这一点。 – aqwert 2010-07-20 10:32:41

1

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

4

我不确定访问调用堆栈的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 {2}ms", 
          eventArgs.Method.ReflectedType.Name, 
          eventArgs.Method, 
          ts.TotalMilliseconds); 
    } 
} 

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

[Logging] 
public void SomeMethod(String p1, String p2, int p3) 
{ 
    //.. 
} 
+0

('System.Diagnostics.StackTrace' /'System.Diagnostics.StackFrame'似乎只提供源文件名和行号信息等,而不是实际的参数。) – stakx 2010-07-20 10:19:31

0

可能无法在某些情况下工作,但应该让你开始:)

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); 
     } 
    } 
} 
+0

修复后添加缺少的名称空间System.Reflection它记下它不告诉什么是价值观。 – 2012-11-29 14:37:07

0

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

- 编辑

此外,使用MethodBase.Name扩展方法,你可以与他们花了作为参数,如下面另一篇文章中所提到的方法您的参数相关联。跟踪所使用的所有方法以及使用哪些参数以及哪种类型是一种方便的方式。

这是否甚至隐约是一个好主意? :)

相关问题