2010-07-20 228 views
22

說我有一個方法:有沒有辦法獲得傳遞給方法的參數數組?

public void SomeMethod(String p1, String p2, int p3) 
{ 

#if DEBUG 
    object[] args = GetArguments(); 
    LogParamaters(args); 
#endif 

    // Do Normal stuff in the method 
} 

有沒有一種方法來檢索傳遞到方法的參數數組,這樣他們可以記錄?

我有大量的方法,並希望避免手動名傳遞參數記錄器,因爲人爲錯誤將不可避免地在蠕動

我猜這將涉及某種形式的反思 - 這沒問題,因爲它只會用於調試目的。

更新

多一點信息:

我不能改變的someMethod的方法簽名,因爲它是暴露一個WebMethod和具有複製遺留系統被仿冒。

遺留系統已經記錄了傳入的參數。要開始使用新的實現將包裝遺留系統,所以我期待記錄參數進入C#版本,以便我可以驗證正確的參數以正確的順序傳入。

我只是在尋找記錄參數值和順序,而不是他們的名字。

+0

有關參數的信息存儲在日誌中?類型,名稱,價值? – devnull 2010-07-20 09:36:55

+0

這意味着你真的在討論_arguments_,而不是_parameters_。 [(例如,參見這裏的關於差異的解釋。)](http://msdn.microsoft.com/en-us/library/9kewt1b3%28VS.80%29.aspx) – stakx 2010-07-20 10:00:31

+0

@stax,是的區別是有意義的。我正在尋找傳遞給方法的參數值。 – 2010-07-20 10:05:35

回答

0

這就是我想出了一個解決方案:

PostSharp或其他AOP的解決方案是不是真的實際在這種情況下,所以很遺憾我不得不放棄這個想法。

看起來雖然可以使用反射來指定參數名稱和類型,但訪問運行時值的唯一方法是使用附加的調試器。

這裏看到更多的信息:

StackOverflow

microsoft.public.dotnet.framework

所以仍然給我留下的是需要這種記錄手工添加〜50個方法問題。

反射救援...

public String GetMethodParameterArray() 
    { 
     var output = new StringBuilder(); 
     output.AppendLine(); 

     Type t = typeof(API); 
     foreach (var mi in t.GetMethods()) 
     { 
       var argsLine = new StringBuilder(); 
       bool isFirst = true; 
       argsLine.Append("object[] args = {"); 
       var args = mi.GetParameters(); 

       foreach (var pi in args) 
       { 
        if (isFirst) 
        { 
         isFirst = false; 
        } 
        else 
        { 
         argsLine.Append(", "); 
        } 
        argsLine.AppendFormat("{0}", pi.Name); 
       } 
       argsLine.AppendLine("};"); //close object[] initialiser 

       output.AppendLine(argsLine.ToString()); 
       output.AppendFormat("Log(\"{0}\",args);", mi.Name); 
       output.AppendLine(); 
       output.AppendLine(); 
      } 
     return output.ToString(); 
    } 

此代碼段遍歷一個類的方法和輸出對象[]數組傳遞到方法和含有該參數的日誌調用的參數初始化和方法名稱。

輸出示例:

object[] args = {username, password, name, startDate, endDate, cost}; 
Log("GetAwesomeData",args); 

該塊然後可以被粘貼到達到所要求的效果的方法的頂部。

它比我所希望的更手動,但它比手動輸入參數好得多,而且更不容易出錯。

0

有與動態類型系統,可以做到這一些功能,但隨後你的類需要從動態基類

繼承
+0

你甚至讀過這個問題嗎?他想記錄所有進入函數的參數並想訪問這些數據。 – Oded 2010-07-20 09:28:56

1

好吧,如果你只是想傳遞的價值觀,你可以欺騙和定義對象數組:

public static void LogParameters(params object[] vals) 
{ 

} 

這將招致值類型拳,也沒有給你任何參數名稱,但是。

說我有一個方法:

public void SomeMethod(String p1, String p2, int p3) 
{ 

#if DEBUG 
    LogParamaters(p1, p2, p3); 
#endif 

    // Do Normal stuff in the method 
} 

更新:遺憾的是反射不會爲你做這一切自動完成。您需要提供的值,但你可以使用反射來提供帕拉姆名稱/類型:

How can you get the names of method parameters?

因此該方法的簽名會更改爲類似:

public static void LogParameters(string[] methodNames, params object[] vals) 
{ } 

然後你就可以強制/假設每個集合中的每個索引都符合,因此methodNames[0]的值爲vals[0]

+0

如果可能的話,我試圖避免在LogParameters調用中寫入參數名稱,因此需要執行以下操作: LogParamaters(GetMethodParams()); GetMethodParams()將返回參數數組。 我並不太在意拳擊的開銷。 – 2010-07-20 09:33:38

+0

我修改了我的答案,但正如其他人所說,有框架可用於將必要的代碼作爲編譯器步驟添加 - 意味着您不需要手動編寫大量代碼。 – 2010-07-20 09:37:43

+0

當然,PostSharp唯一的缺點是它不是免費的,你仍然需要編輯代碼來放置屬性。 – 2010-07-20 09:42:16

7

如果您使用Postsharp,則可以簡單地向要記錄的方法添加屬性。在這個屬性中,你可以編寫日誌代碼,也可以提供你需要的參數。這就是所謂的橫切關注點和AOP(面向方面​​的編程)

+0

+1。我打算寫一些關於自動重寫編譯器的CIL輸出以添加日誌的方法,但只使用已建立的產品可能會更容易。 – stakx 2010-07-20 10:04:13

+0

同樣做到以上是快速和容易做到這一點。 – aqwert 2010-07-20 10:32:41

1

好吧params幫助與日誌調用,但不會幫助現有的方法簽名。使用AOP framework進行記錄可能是一種更高效的方法?

4

我不確定訪問調用堆棧的API是否提供獲取參數列表的方法。

但是有些方法可以注入IL來攔截方法調用並執行自定義代碼。

我經常使用的庫是由Gael Fraiteur提供的PostSharp,它包含一個運行postbuild並根據您使用的Aspects在您的輸出程序集中注入IL的應用程序。有些屬性可以用來修飾程序集,類型或單個方法。例如:

[Serializable] 
public sealed class LoggingAttribute : OnMethodBoundaryAspect 
{ 
    public override void OnEntry(MethodExecutionArgs eventArgs) 
    { 
     Console.WriteLine("Entering {0} {1} {2}", 
          eventArgs.Method.ReflectedType.Name, 
          eventArgs.Method, 
          string.Join(", ", eventArgs.Arguments.ToArray())); 

     eventArgs.MethodExecutionTag = DateTime.Now.Ticks; 
    } 

    public override void OnExit(MethodExecutionArgs eventArgs) 
    { 
     long elapsedTicks = DateTime.Now.Ticks - (long) eventArgs.MethodExecutionTag; 
     TimeSpan ts = TimeSpan.FromTicks(elapsedTicks); 

     Console.WriteLine("Leaving {0} {1} after {2}ms", 
          eventArgs.Method.ReflectedType.Name, 
          eventArgs.Method, 
          ts.TotalMilliseconds); 
    } 
} 

在這之後你可以裝飾你想要使用此屬性的方法:

[Logging] 
public void SomeMethod(String p1, String p2, int p3) 
{ 
    //.. 
} 
+0

('System.Diagnostics.StackTrace' /'System.Diagnostics.StackFrame'似乎只提供源文件名和行號信息等,而不是實際的參數。) – stakx 2010-07-20 10:19:31

0

可能無法在某些情況下工作,但應該讓你開始:)

class Program 
{ 
    static void Main(string[] args) 
    { 
     M1("test"); 
     M2("test", "test2"); 
     M3("test", "test2", 1); 

     Console.ReadKey(); 
    } 

    static void M1(string p1) 
    { 
     Log(MethodBase.GetCurrentMethod()); 
    } 

    static void M2(string p1, string p2) 
    { 
     Log(MethodBase.GetCurrentMethod()); 
    } 

    static void M3(string p1, string p2, int p3) 
    { 
     Log(MethodBase.GetCurrentMethod()); 
    } 

    static void Log(MethodBase method) 
    { 
     Console.WriteLine("Method: {0}", method.Name); 
     foreach (ParameterInfo param in method.GetParameters()) 
     { 
      Console.WriteLine("ParameterName: {0}, ParameterType: {1}", param.Name, param.ParameterType.Name); 
     } 
    } 
} 
+0

修復後添加缺少的名稱空間System.Reflection它記下它不告訴什麼是價值觀。 – 2012-11-29 14:37:07

0

只要你知道什麼類型的期望,你可以將它們記錄在SQL數據庫中。編寫一個執行類型檢查的方法,然後用參數(參數)值填充適當的DB列。如果您有自定義類型,則可以使用類型名稱並將其作爲字符串保存在其自己的特殊列中。

- 編輯

此外,使用MethodBase.Name擴展方法,你可以與他們花了作爲參數,如下面另一篇文章中所提到的方法您的參數相關聯。跟蹤所使用的所有方法以及使用哪些參數以及哪種類型是一種方便的方式。

這是否甚至隱約是一個好主意? :)

相關問題