繁体   English   中英

如何获取调用方法的参数值?

[英]How can I get the values of the parameters of a calling method?

问题

我正在编写一些代码,需要能够从调用类的方法中获取参数的 我知道如何获得 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");
            }
        }
    }
}

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

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

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

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

要清楚,简单的反射技术不能完全通用地实现您的愿望

微软的 Jonathan Keljo 在这个新闻组帖子中说:

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

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

乔纳森

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

是的,你可以这样做。

您需要做的是使用 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...
                }
            }

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

看这里:

你能在 C# 中获得堆栈上的变量列表吗?

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

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

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

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

暂无
暂无

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

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