22

问题

我正在编写一些代码,这些代码需要能够从调用类的方法中获取参数的值。我知道如何一直到 ParameterInfo[] 数组,但我不知道如何获取这些值。这甚至可能吗?

如果是,我认为它与使用 MethodInfo 对象中的 MethodBody 属性有关,它允许您检查 IL 流,包括属性,但我不知道该怎么做,我还没有找到Google 上的适用代码。

代码

// Finds calling method from class that called into this one
public class SomeClass
{
    public static void FindMethod()
    {
        for (int i = 1; i < frameCount; i++)
        {
            var frame = new StackFrame(i);
            var methodInfo = frame.GetMethod();
            if (methodInfo.DeclaringType != this.GetType())
            {
                string methodName = frame.GetMethod().Name;
                var paramInfos = methodInfo.GetParameters();

                // Now what?? How do I get the values from the paramInfos

                break;
            }
            else if (i == frameCount - 1)
            {
                throw new TransportException("Couldn't find method name");
            }
        }
    }
}
4

6 回答 6

19

如果不自己自省堆栈,您就无法做到这一点(这很脆弱,因为许多优化可能意味着堆栈帧不是您所期望的,或者甚至传递的参数实际上不是方法签名所暗示的(这完全有可能一个优化的 JIT 编译器,以发现您仅使用对象/结构的子字段并传递它)。

ParameterInfo 只是告诉您编译时方法的签名,而不是传递的值。

自动实现这一点的唯一现实方法是通过代码注入(通过 AOP 之类的东西)来创建数据并根据分析 IL 来做你想做的事情。

这通常不是一个好主意,如果您需要调试某些东西,请使用调试器,如果您需要记录某些内容,请明确说明您正在记录的内容。

需要明确的是,简单的反思技术不能完全概括地实现你想要的

于 2009-01-29T17:41:59.003 回答
5

Microsoft 的 Jonathan Keljo 在此新闻组帖子中说:

不幸的是,今天从调用堆栈中获取参数信息的唯一简单方法是使用调试器。如果您尝试将此作为应用程序中错误记录的一部分,并且您计划将错误日志发送回您的支持部门,我们希望您将来可以为此目的使用小型转储。(今天,使用托管代码的小型转储有点问题,因为它没有包含足够的信息,甚至默认情况下都无法获得堆栈跟踪。带有堆的小型转储更好,但如果你知道我的意思,它就不是那么“迷你”了。)

纯粹主义者会说,允许人们编写可以从调用堆栈上其他地方的函数获取参数的代码将鼓励他们打破封装并创建在面对变化时非常脆弱的代码。(您的场景没有这个特殊问题,但我听说过其他对此功能的请求。无论如何,这些请求中的大多数都可以通过其他方式解决,例如使用线程存储。)但是,更重要的是会有安全隐患其中——允许插件的应用程序将面临这些插件从堆栈中获取敏感信息的风险。我们当然可以将该功能标记为需要完全信任,但这会使它在我听说过的几乎所有场景中都无法使用。

乔纳森

所以......我想简短的回答是“我不能”。太糟糕了。

于 2009-01-29T17:40:48.397 回答
5

是的,你可以这样做。

您需要做的是使用 IL 反汇编程序(可在 System.Reflection.Emit 命名空间中实现)来查找包含您要查找的参数值的操作数。

从这个 SO 问题开始:C# 反射和查找所有引用

然后使用答案中提到的类(来自 Mono.Reflection)进行检查。像这样的东西:

            var instructions = method.GetInstructions();
            foreach (var instruction in instructions)
            {
                var methodInfo = instruction.Operand as MethodInfo;
                if(methodInfo == null)
                {
                    continue;
                }
                if (instruction.OpCode.Name.Equals("call") && methodInfo.Name.Equals("YourMethodHere"))
                {
                    var value = (CastToMyType)instruction.Previous.Operand;
                    // Now you have the value...
                }
            }
于 2011-05-22T18:25:12.390 回答
1

你不能用 StackFrame 或 StackTrace 来做到这一点。但是,您可以使用一些拦截框架(例如 Spring.NET 中的 AOP 东西),以便您可以获取参数值。

于 2009-01-29T17:33:31.953 回答
0

看这里:

您可以在 C# 中获取堆栈上的变量列表吗?

根据那里对我的回答的所有评论,我认为这是不可能的。PropertyInfo 类确实有一个 GetValue 方法,但这需要您拥有一个要从中获取值的实际对象。

于 2009-01-29T17:43:02.853 回答
0

不确定这是否算作一个解决方案,但适用于我所拥有的特定情况,我想在每次修改浮点数时以最少的代码更改进行记录,

读取堆栈跟踪行上的文件以找出参数

public static Score operator +(Score x,float y) {
    var st = new StackTrace(true);
    var sf = st.GetFrame(1);                
    string paramName = File.ReadLines(sf.GetFileName()).ElementAtOrDefault(sf.GetFileLineNumber()-1).Split(new[] { "+=" }, StringSplitOptions.None)[1];
            
    x.DebugString += (paramName+" "+y);
    x.DebugString += System.Environment.NewLine;
            
    x.val += y;
    return x;
}
            
            
void Main(){
    Score score = new Score();
    float firstScore = 2;
    float secondScore = -13;
    score+=firstScore;
    score+=secondScore;
    Console.WriteLine(score.DebugString);
}
        
Output : 
firstscore  2
secondScore -13
于 2020-09-07T19:50:57.860 回答