繁体   English   中英

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

[英]Is there a way to get an array of the arguments passed to a method?

说我有一个方法:

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

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

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

我不确定访问调用堆栈的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) 
{
   //..
}

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

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 
 } 

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

如何获取方法参数的名称?

所以方法sig会改为:

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

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

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

当然可以......查看这篇文章,它获得了参数的实际值。 如何枚举传递的方法参数

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

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

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

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

-编辑

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

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

以下是我提出的解决方案:

在这种情况下,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();
    }

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

示例输出:

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

然后可以将该块粘贴到方法的顶部以实现所需的效果。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM