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

本文关键字:调用 方法 获取 参数 | 更新日期: 2023-09-27 18:30:44

这个问题与我之前的问题有关 如何获取 IDictionary<string,> 参数 上一个方法 在 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;
        }
    }
}

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

你的代码可能是一个死胡同。堆栈帧中没有任何内容可用于获取参数值。

但是,完全可以完成此任务。您想要的内容与编写探查器非常相似。您可能希望将代码注入到要记录的任何方法中,以矢量化其参数。假设您从这样的类开始:

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 以附加到所需的任何例程。

有关详细信息,请参阅文章使用 .NET Framework 分析 API 即时重写 MSIL 代码

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

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

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

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

AOP是你的朋友。