2011-05-08 26 views
5

我想寫一個可以在任何方法中調用的可重用函數來記錄所有局部變量的快照。例如:C#可轉儲函數轉儲當地變量的當前值

void somemethod() 
    { 
     int a = 1; 
     string s = "something"; 
     dumpLocalVariables("step 1", MethodInfo.GetCurrentMethod(), this); 

     a++; 
     string t = s + "else"; 
     dumpLocalVariables("step 2", MethodInfo.GetCurrentMethod(), this); 
    } 

我想獲得一個控制檯輸出是這樣的:

step 1 
    Int32 a = 1 
    String s = something 
step 2 
    Int32 a = 2 
    String s = something 
    String t = somethingelse 

我想避免提供局部變量名的特定列表。

我能找到的最接近的是MethodInfo.GetCurrentMethod().GetMethodBody().LocalVariables,但我不知道如何使用反射訪問局部變量的值。

void dumpLocalVariables(string context, MethodBase currentMethod, object obj) 
{ 
    Console.WriteLine(context); 
    MethodBody methodBody = currentMethod.GetMethodBody(); 
    foreach (LocalVariableInfo lvi in methodBody.LocalVariables) 
    { 
     string variableType = lvi.LocalType.Name; 
     // how do I get this? 
     string variableName = "variableNameHere"; 
     // how do I get this?  
     string variableValue = "variableValueHere"; 
     Console.WriteLine(" " + variableType + " " + variableName + 
      " = " + variableValue); 
    } 
} 

反射API似乎非常適合靜態分析,但不適用於像這樣的動態分析。例如,在首次致電dumpLocalVariables期間,變量t不在範圍內,但它仍出現在MethodBodyLocalVariables屬性中。

我懷疑有一個調試API,我忽略了。 Developer Studio如何在斷點處填充「本地」選項卡?有沒有辦法在運行時做類似的事情?

編輯:

我可以在我的例子類使用像ldloc.0和ldloc.1 IL代碼去的第一和第二局部變量ILSpy看到。

.locals init (
    [0] int32 a 
    [1] string s 
    [2] string t 
) 

後來

IL_001b: ldloc.0 // this is a 
IL_001c: ldc.i4.1 
IL_001d: add 
IL_001e: stloc.0 
IL_001f: ldloc.1 // this is s 
IL_0020: ldstr "else" 
IL_0025: call string string::Concat(string, string) 
IL_002a: stloc.2 // this is t 

也許我可以用某種代理樣的機制,讓我做同樣的事情?我不介意如果對我的可重用方法的調用是混亂的,我只想要一些我可以粘貼到任何代碼塊而不需要很多手動編輯的東西。

+1

部分「我懷疑的是,我可以俯瞰調試API」:'System.Diagnostics.Debugger' – 2011-05-08 18:34:10

+0

@Henk:你可以澄清?我不知道'Debugger'類除了'IsAttached'和'Break()'以外還有其他用處。 – Groo 2011-05-08 18:44:19

+1

可能的重複:[有沒有辦法在c#中的運行時檢查堆棧變量?](http://stackoverflow.com/questions/5130414/is-there-a-way-to-examine-the-stack-variables在運行時在C),[C#如何在運行時轉儲所有變量和當前值](http://stackoverflow.com/questions/1552478/c-how-to-dump-all-variables-current-值-期間的運行時)。 – Groo 2011-05-08 18:49:39

回答

7

請參閱此相關的問題:

Is there a simple way to obtain all the local variables in the current stack frame in C# (or CIL)

簡短的回答是:因爲他們堆棧在運行時直接分配你不能得到局部變量的值,因此不可通過反射獲得。唯一的方法就是通過調試器API來做到這一點,這遠非瑣碎。此外,這隻有在您的自定義調試器實際連接到進程時纔有效。

更好,更可行的選擇可能是通過組裝編織。你說你不想讓方法在記錄它們的值時知道要訪問的局部變量的具體名稱。我建議建立兩種方法:

static void LogVariables(); 

static void LogVariables(params string[] names, params object[] values); 

添加調用的程序集織造例程,換出第一LogVariables與第二調用後生成的任務,但明確規定變量名/值的方法。你可以編寫這個例程來使用Mono Cecil修改程序集(還有其他工具可以做到這一點)。

http://www.mono-project.com/Cecil

+0

這是迄今爲止最簡單的答案,但不是我一直在尋找的。我仍然想提出一些可以以「正常」方式插入和編譯的內容。 – hsmiths 2011-05-08 23:21:26

+0

我想沒有更多的想法,所以我不情願接受這個答案。我認爲在運行時使用cecil程序集編寫某些東西是可行的,以執行這種日誌記錄,而不需要進行後編譯調整。 – hsmiths 2011-05-09 22:53:38

+0

如果有可能獲得局部變量的列表,爲什麼不能通過Reflection.Emit生成動態代碼來轉儲它的值? – devi 2013-09-13 08:11:41

2

有可能通過使用外部調試器託管代碼。請參閱「託管調試器樣本」瞭解它是如何完成的:http://blogs.msdn.com/b/jmstall/archive/2004/09/30/236281.aspx(包括樣本的鏈接和更多信息)

+0

感謝您的鏈接。 mdbg的東西是有趣的閱讀。預先需要做更多的工作,但我懷疑我可以根據這個做點什麼來做我想做的事。我不認爲我會一個人承認需要傾倒當地人的模式,並且每次都要做一次性的事情。 – hsmiths 2011-05-08 23:21:17