2010-05-17 84 views
5

我使用log4net的,我們有我們的代碼有很多這樣的:聲明靜態成員的類裝飾器(例如,用於log4net)?

public class Foo { 
    private static readonly ILog log = LogManager.GetLogger(typeof(Foo)); 
    .... 
} 

一個缺點是,它意味着我們遍佈粘貼這10個字的部分,每一個現在,然後有人忘記更改類名稱。該log4net FAQ也提到了這個另一種可能性,這更加詳細:

public class Foo { 
    private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
    ... 
} 

是否可以寫一個裝飾來定義呢?我真的很想說:

[LogMe] // or perhaps: [LogMe("log")] 
public class Foo { 
    ... 
} 

我在其他語言做過類似的事情,但從來沒有像C#這樣的靜態編譯語言。我可以從裝飾者定義類成員嗎?

編輯:heh。我是一名Lisp程序員。我很欣賞切換語言的建議,但實際上,如果我要切換語言以實現更好的元編程功能,那麼我會一路走向Lisp,而不是被半路而廢。不幸的是使用不同的語言不是這個項目的一個選項。

回答

1

在.NET中不是裝飾/活動組件影響他們所屬的類/成員。 屬性是元數據,可以用反射來回顧。 C#中沒有類似於裝飾器的工具,儘管有AOP解決方案可以用你想要的擴展.NET。最簡單的解決方案可能只是將該行復制到每個類。

+0

這是真的,我說錯。不過,我想我希望能夠使用某種全局初始化鉤子來將屬性轉換爲成員。 – Ken 2010-05-17 21:55:31

5

這正是AOP - 面向方面編程的一項任務。看看PostSharp,這是一個.NET AOP框架,它可以讓你做到你想要的。

這可以通過修改(或編譯)後編譯中的IL代碼,並將日誌記錄方面添加到裝飾方法中。

編輯:看來,PostSharp現在是一個商業產品。如果您正在尋找開源(免費)解決方案,我建議LinFu AOP

0

如果這是ASP.NET或Windows窗體,那麼您可以創建一個基本的頁面或基本窗體,以表示所有窗體/頁面派生自。您可以在該級別聲明此成員,並可能實現您的願望。

+0

我甚至不完全確定這些是什麼。我只是在這裏使用普通的C#。 – Ken 2010-05-17 21:57:23

+0

子類化某個基類只是爲了減少一行代碼*非常*代碼味道不好!如果您需要一個類來繼承不同的給定基類,該怎麼辦?看看Tim Jarvis的解決方案,它至少不需要子類化,但使用接口。 – chiccodoro 2010-10-08 13:00:46

0

您可以在PostSharp或另一個面向方面編程工具的幫助下爲此實現解決方案。

在Boo中使用宏將會很微不足道,但這對你的情況並沒有多大幫助。

0

也許一個簡單的方法是在接口編寫擴展方法,那麼你的類只需要「落實」(但不是真的,因爲擴展做的IMPL)接口

public class Foo : IGetLog<Foo> 
{ 

} 

擴展就像是....

public interface IGetLog<T> { } 

    public static class IGetLogExtension 
    { 
    public static ILog GetLogger<T>(this IGetLog<T> getLog) 
    { 
     return LogManager.GetLogger(typeof(T)); 
    } 
    } 
+0

有趣。這不是*太糟糕。我不是瘋了它,因爲它不是* *很乾,但是它不太打字,和重複是在同一條線上,所以它不太可能用錯誤的類名被錯誤地粘貼。 – Ken 2010-05-17 22:02:53

+0

確實如此,但請記住屬性解決方案還需要您想要支持的每個類的代碼行。 – 2010-05-17 22:48:54

+0

強制每個類別的項目來實現某個接口只是因爲我們不會登錄東西在我的鼻子難聞。 – chiccodoro 2010-10-08 12:58:06

1

我們包裝了log4net,所以我們可以輕鬆地將其切換出來。這是我們未來可能改變主意的事情。

雖然我們不這樣做,你可能會採取的性能損失這樣做....,我很猶豫,甚至建議,因爲我不知道這是一個好主意...... ..你可以......如果你覺得噁心......包裝log4net並在你的包裝中做這樣的事情。

var callingType = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType 

如果你對如何記錄日誌很敏感,那麼只有當你需要日誌消息時纔會產生成本。

+0

這個程序是一個(相當大的)數據庫的前端。即使是快速的數據庫查詢將主宰任何性能配置文件,我幾乎肯定會這麼做。 :-) – Ken 2010-05-17 22:01:14

+0

@Ken在最糟糕的情況下,您會爲每條日誌消息生成一個堆棧跟蹤。您可以使用相同的方法在您展示的靜態構造函數中進行初始化,並避免每條消息的開銷。 – Mark 2010-05-18 12:48:25

+0

馬克,恕我直言,log4net的*是*其實只不過是包裝其他 - 圍繞在圖書館,你可以選擇你自己的,每當你想更換交付預定實施的包裝。 – chiccodoro 2010-10-08 13:03:24

3

我們用幾乎相同的東西:

private static readonly ILog Logger = LogManager.GetLogger(); 

如下實現此方法是:

[MethodImpl(MethodImplOptions.NoInlining)] 
public static ILog GetLogger() 
{ 
    Type t; 
    try { t = new StackFrame(1, false).GetMethod().DeclaringType; } 
    catch { t = typeof(UnknownObject); } 
    return GetLogger(t); 
} 
private static class UnknownObject { } 

它仍然被證明是更多的,我很願意去通過一個痛苦的。我更喜歡使用靜態方法進行日誌記錄的靜態對象。如果沒有獲取文件信息,獲取調用方法並不昂貴。與僅僅調用Log4Net獲得調用類型的開銷相比,沒有什麼(取決於所使用的記錄器)。

+0

有趣!我絕對想要現在就做這樣的事情。 – Ken 2010-05-17 21:59:11