2011-03-26 52 views
6

當記錄時,你總是糾纏在字符串文字中。C#通過任何方法作爲參數

我解決了這個很好的屬性,字段和變量傳遞一個Expression<Func<T>> expression(如解釋here),所以你可以做這樣的事情:

public void Demo(string someArgument) 
{ 
    LogFrameWork.LogLine("Demo"); // goal is to get rid of these string literals 
    LogFramework.Log(() => someArgument); 
} 

我想要做的方法Demo本身類似的東西:

public void Demo(string someArgument) 
{ 
    LogFramework.Log(this.Demo); 
} 

我試過這樣的事情:

public static void Log(Delegate method) 
{ 
    string methodName = method.Method.Name; 
    LogLine(methodName); 
} 

這:

public static void Log(Action method) 
{ 
    string methodName = method.Method.Name; 
    LogLine(methodName); 
} 

,但我得到這樣的編譯器錯誤:

​​

我介紹了一堆使用Func<...>Action<...>過載,但聽起來過於複雜。

有沒有辦法用任何數量的參數和可選的結果來覆蓋這個方法?

--jeroen

PS:我覺得this question可能有一定的相關性在這裏,但沒有答案,這讓我一個「啊哈」的感覺:-)

+1

但是,我理解你的問題,難道你不認爲你會更容易和更可擴展的一些AOP框架?您可以創建自定義屬性來標記在調用時應該記錄的方法。 – empi 2011-03-26 16:21:43

+1

我過去使用過AOP,它導致編譯/鏈接時間猛增。由於.NET已經有了更高的編譯/鏈接時間,我寧願避免這種情況。 – 2011-03-26 16:29:38

+1

針對我在表達式樹中看到的這個常見問題的每個解決方案都使用了多重重載方法,這也有點尷尬,因爲您需要包含參數。如果我要編寫手動跟蹤日誌記錄,我通常只是將方法名稱作爲字符串常量傳遞,並依靠Resharper來提醒我保持同步。 – 2011-03-26 16:33:52

回答

3

不是試圖將方法作爲參數傳遞給記錄器,而是從使記錄器識別調用方法的角度來看待它。

下面是一個(僞)例如:

Logger類

public void Debug(string message) 
{ 
    message = string.Format("{0}: {1}", GetCallingMethodInfo(), message); 
    // logging stuff 
} 

/// <summary> 
/// Gets the application name and method that called the logger. 
/// </summary> 
/// <returns></returns> 
private static string GetCallingMethodInfo() 
{ 
    // we should be looking at the stack 2 frames in the past: 
    // 1. for the calling method in this class 
    // 2. for the calling method that called the method in this class 
    MethodBase method = new StackFrame(2).GetMethod(); 
    string name = method.Name; 
    string type = method.DeclaringType.Name; 

    return string.Format("{0}.{1}", type, name); 
} 

使用該記錄器Anywhere的:

// resides in class Foo 
public void SomeMethod() 
{ 
    logger.Debug("Start"); 
} 

從記錄程序的輸出將是:Foo.SomeMethod: Start

+0

+1;感謝詳細解釋[解決方案](http://stackoverflow.com/questions/5443521/c-pass-any-method作爲一個參數/ 5443606#5443606)[Josh G](http://stackoverflow.com/users/64329/josh-g)提出的。我確實知道他的意思,但有一個更詳細的例子在手將會幫助未來的用戶很多 – 2011-03-26 16:38:57

+1

這是我在幾年前使用'MS企業日誌庫'編寫的日誌庫。這個解決方案几乎解決了你想要達到的目標,我想,你需要記錄器知道什麼方法稱爲記錄器。關於這一點的好處是使用'DeclaringType',你最終會得到一個完整的名稱空間給方法。而且不必擔心將方法名稱添加爲日誌消息的一部分。 – 2011-03-26 17:04:21

+1

我接受了你的答案,因爲它在呼叫站點創建了最乾淨的代碼。沒有令人費解的表情。尼斯。 – 2011-04-13 22:36:22

0

您可以定義一個委託,然後接受該委託作爲參數。

public delegate void DemoDelegate(string arg); 

public void MyMethod(DemoDelegate delegate) 
{ 
    // Call the delegate 
    delegate("some string"); 
} 

可以調用的MyMethod這樣的:

MyMethod(delegate(string arg) 
{ 
    // do something 
}); 

void MethodThatTakesAString(string value) 
{ 
    // do something 
} 

MyMethod(MethodThatTakesAString); 

請參閱此鏈接瞭解更多信息:

http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx

+1

我瞭解代表,但沒有看到這將如何幫助我的任何方法與任何數量的參數。請詳細說明,因爲我可能會查看一些非常明顯的內容:-) – 2011-03-26 16:31:46

5

你也可以做到這一點而不通過System.Diagnostics.StackTrace使用ExpressionTree。

StackTrace trace = new StackTrace(); 

然後:

trace.GetFrame(0).GetMethod().Name 

要獲得的MethodInfo,然後命名當前的方法,或:

trace.GetFrame(1).GetMethod().Name 

獲取調用方法。

+0

+1;有趣!將調查這一點。感謝您在箱子外面思考。這很可能也會幫助我以自動方式獲取參數。 – 2011-03-26 16:36:22

+1

這尤其是非高性能的,並且存在將幀優化掉的風險。 – 2011-03-26 16:39:09

+0

@Kirk:表達式也被稱爲非常昂貴,你可以詳細說明'正在優化'部分;你的意思是內聯嗎? – 2011-03-26 16:40:59

0

試試這個:

/// <summary> 
/// Trace data event handler delegate. 
/// </summary> 
/// <returns>The data to write to the trace listeners</returns> 
public delegate object TraceDataEventHandler(); 

public static class Tracing 
{ 

    /// Trace a verbose message using an undefined event identifier and message. 
    /// </summary> 
    /// <param name="message">The delegate to call for the trace message if this event should be traced.</param> 
    [Conditional("TRACE")] 
    public static void TraceVerbose(TraceMessageEventHandler message) 
    { 
     ... your logic here 
    } 
} 

然後你就可以做...

Tracing.TraceVerbose(() => String.Format(...)); 

我希望我已經正確理解你的問題......這是否做你想要的?

5

這比看起來要難得多。我認爲你最好用通用的Func和Action重載,但是有一種方法可以用表達式樹來完成。下面是LINQPad一個例子:

public static void Log(Expression<Action> expr) 
{ 
    Console.WriteLine(((MethodCallExpression)expr.Body).Method.Name); 
} 

void Main() 
{ 
    Log(() => DoIt()); 
    Log(() => DoIt2(null)); 
    Log(() => DoIt3()); 
} 

public void DoIt() 
{ 
    Console.WriteLine ("Do It!"); 
} 

public void DoIt2(string s) 
{ 
    Console.WriteLine ("Do It 2!" + s); 
} 

public int DoIt3() 
{ 
    Console.WriteLine ("Do It 3!"); 
    return 3; 
} 

此輸出:

DoIt 
DoIt2 
DoIt3

注意,我不得不使用lambda表達式並調用日誌方法時指定僞參數。

這是基於Fyodor Soikin's excellent answer

+0

感謝您的回答;我擔心它會變成這樣的東西,但確認它是很好的。 – 2011-03-27 19:48:08