0

这个问题与我之前的问题How to get a IDictionary<string, object> of the parameters previous method called in C#有关?. 我写了代码,但仍然缺少一块。如何从参数中获取值?

如果执行以下代码,则输出仅显示参数的名称,而不显示值。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Question {
    internal class Program {
        public static void Main(string[] args) {
            var impl = new Implementation();
            var otherClass = new OtherClass { Name = "John", Age = 100 };
            impl.MethodA(1, "two", otherClass);
        }
    }

    internal class Implementation {
        public void MethodA(int param1, string param2, OtherClass param3) {
            Logger.LogParameters();
        }
    }

    internal class OtherClass {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    internal class Logger {
        public static void LogParameters() {
            var parameters = GetParametersFromPreviousMethodCall();
            foreach (var keyValuePair in parameters)
                Console.WriteLine(keyValuePair.Key + "=" + keyValuePair.Value);
        }

        private static IDictionary<string, object> GetParametersFromPreviousMethodCall() {
            var stackTrace = new StackTrace();
            var frame = stackTrace.GetFrame(2);
            var method = frame.GetMethod();

            var dictionary = new Dictionary<string, object>();
            foreach (var parameterInfo in method.GetParameters())
                dictionary.Add(parameterInfo.Name, parameterInfo.DefaultValue);
            return dictionary;
        }
    }
}
4

2 回答 2

3

您的代码可能是死胡同。堆栈帧中没有任何内容可以让您获取参数值。

但是,完全可以执行此任务。您想要的与编写分析器非常相似。您可能希望将代码注入任何您想要记录的方法中以向量化其参数。假设您从这样的课程开始:

public class ParameterBlob {
    public ParameterInfo Info { get; set; }
    public object Value { get; set; }
}

假设你有一个这样的方法:

public static void LogMethodCall(MethodInfo method, param ParameterBlob[] parameterBlobs) { /* ... */ }

这或多或少是您要注入的内容:

MethodInfo methodInfo = MyLogging.GetMyMethodInfo();
ParameterBlob[] blobs = new ParameterBlobs[MyLogging.GetMyParameterCount(methodInfo);
ParameterBlob blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, 0);
blob.Value = param0; // More on this
blobs[0] = blob;
blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, 1);
blob.Value = param1; // More on this
blobs[1] = blob;
// ...
blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, n);
blob.Value = paramn; // more on this
blobs[n] = blob;
MyLogging.LogMethodCall(methodInfo, blobs);

那么那些说“更多关于这个”的行?你实际上不能写它们。但是您可以编写一个示例例程来引用它自己的参数来执行此操作。您拥有的是ldarg指令和stloc指令(以及介于两者之间的其他一些指令)。关键是,用 C# 编写它,然后使用编译器和ILDASM向您显示一个参数所需的正确代码,然后您可以编写一个例程来为您生成该CIL,然后将其插入 .NET分析 API 以附加到您想要的任何例程。

有关更多信息,请参阅文章Rewrite MSIL Code on the Fly with the .NET Framework Profiling API

您可能还想使用属性将方法标记为可记录或不可记录。

唯一的问题是您必须具有运行时访问权限才能执行此操作,而您可能没有。你完全不走运吗?不。

通过使用Cecil,您可以在运行之前访问整个程序集并对其进行预处理以为您注入日志记录调用。Cecil 非常简单,重写任何程序集以包含日志调用应该需要几天的时间。如果您可以先验地知道您正在目标程序集中执行此操作并且已经设置了适当的引用,则更少。本质上,您将访问程序集中的每个方法,如果它是可记录的,您将注入 CIL 以记录其所有参数,就像上面的示例一样。

于 2012-04-09T20:50:59.040 回答
2

根据先前的问题(如何获取调用方法的参数值?),通过 StackTrace 是不可能的。

AOP 是你的朋友。

于 2012-04-09T20:38:54.140 回答