簡體   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