如何从C#中的先前方法调用中获取参数值?

时间:2012-04-09 20:22:28

标签: c# .net reflection

此问题与我之前的问题 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;
        }
    }
}

2 个答案:

答案 0 :(得分:2)

根据先前的问题(How can I get the values of the parameters of a calling method?),通过StackTrace无法实现。

AOP是你的朋友。

答案 1 :(得分:2)

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

但是,完全可以完成此任务。你想要的与编写探查器非常相似。您可能希望将代码注入要记录的任何方法以引导其参数。假设你开始使用这样的课程:

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,您可以在运行之前访问整个程序集并对其进行预处理以为您注入日志记录调用。塞西尔非常简单,应该花几天时间重写任何程序集以包含日志记录调用。如果您可以先了解在目标程序集中执行此操作并已设置适当的引用,则会少一些。基本上,您将访问程序集中的每个方法,如果它是可记录的,您将注入CIL以记录其所有参数,就像上面的示例一样。