2010-05-17 9 views
5

Sto utilizzando log4net, e abbiamo un sacco di questo nel nostro codice:Decoratore di classe per dichiarare membro statico (ad es. Per log4net)?

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

Uno svantaggio è che significa che stiamo incollando questa sezione 10-word in tutto, e ogni ora e poi qualcuno dimentica di cambiare il nome della classe. Il log4net FAQ menziona anche questa possibilità alternativa, che è ancora più dettagliato:

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

È possibile scrivere un decoratore per definire questo? Mi piacerebbe davvero dire semplicemente:

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

Ho fatto cose simili in altre lingue, ma mai un linguaggio staticamente compilato come C#. Posso definire i membri della classe da un decoratore?

Modifica: Heh. Sono un programmatore Lisp. Apprezzo i suggerimenti per cambiare lingua, ma in realtà, se avessi intenzione di passare da una lingua all'altra per migliorare le capacità di metaprogrammazione, andrei fino a Lisp invece di esserne per metà. Sfortunatamente l'uso di una lingua diversa non è un'opzione su questo progetto.

risposta

1

Attributes in .NET non sono decoratori/componenti attivi che influenzano la classe/membro a cui sono associati. Gli attributi sono meta-dati che possono essere recuperati con la riflessione. Non esiste una funzione simile a un decoratore in C#, anche se ci sono soluzioni AOP che estendono .NET con quello che vuoi. La soluzione più semplice è probabilmente quella di copiare semplicemente quella linea in ogni classe.

+0

Questo è vero, e ho misspoke. Comunque, immagino di sperare in qualche tipo di hook di inizializzazione globale che possa essere usato per convertire gli attributi in membri. – Ken

5

Questo è esattamente un compito per AOP - Aspect Oriented Programming. Date un'occhiata a PostSharp, è un framework AOP .NET, vi permetterà di fare esattamente quello che volete.

Questo funziona modificando (o tessendo) il codice IL in post-compilazione e aggiungendo l'aspetto di registrazione al metodo decorato.

MODIFICA: Sembra che PostSharp sia ora un prodotto commerciale. Se stai cercando una soluzione open source (gratuita), suggerisco LinFu AOP.

0

Se si tratta di ASP.NET o Windows Form, è possibile creare una pagina di base o un modulo di base da cui derivano tutti i moduli/pagine. Puoi dichiarare questo membro a quel livello e probabilmente ottenere ciò che desideri.

+0

Non sono nemmeno del tutto sicuro di cosa siano. Sto usando semplicemente C# qui. – Ken

+0

La sottoclasse di una determinata classe base solo per ridurre il codice da una linea è * molto * cattivo odore di codice! Cosa succede se hai bisogno di una classe per sottoclasse una classe base diversa, data? Guarda la soluzione di Tim Jarvis che almeno non richiede sottoclassi ma utilizza un'interfaccia. – chiccodoro

0

Probabilmente si potrebbe implementare una soluzione con l'aiuto di PostSharp o un altro strumento di programmazione orientata agli aspetti.

Sarebbe piuttosto banale usare una macro in Boo, ma questo non aiuterebbe molto la tua situazione.

0

Forse un modo semplice sarebbe quella di scrivere un metodo di estensione in un'interfaccia, quindi la classe solo bisogno di "attuare" (ma non tanto perché l'estensione fa l'impl) l'interfaccia

public class Foo : IGetLog<Foo> 
{ 

} 

L'estensione qualcosa di simile a....

public interface IGetLog<T> { } 

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

Interessante. Non è male. Non ne sono pazzo perché non è * abbastanza * SECCO, anche se è meno digitante, e la ripetizione è sulla stessa linea, quindi è meno probabile che venga erroneamente incollata con un nome di classe errato. – Ken

+0

Vero, ma ricorda che una soluzione di attributo richiede anche la riga di codice su ogni classe che vuoi supportare. –

+0

Forzare ogni classe di un progetto per implementare una determinata interfaccia solo perché non registreremo qualcosa in esso ha un cattivo odore nel naso. – chiccodoro

1

Abbiamo eseguito il wrapping di log4net in modo da poterlo sostituire con facilità. È qualcosa su cui potremmo davvero cambiare idea in futuro.

Mentre non stiamo facendo questo, e probabilmente farai un colpo di performance per farlo ... e sono davvero esitante a suggerirlo anche perché non sono sicuro che sia una buona idea ... ... potresti ... se ti sentissi abbastanza diabolico ... avvolgere log4net e fare qualcosa del genere nel tuo involucro.

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

Se sei intelligente su come stai registrando si potrebbe sostenere solo quel costo quando è necessario il messaggio di registro.

+0

Questo programma è un front-end per un database (piuttosto pesante). Sono quasi sicuro che anche le query di database veloci domineranno qualsiasi profilo di prestazione. :-) – Ken

+0

@Ken Sarai, nel peggiore dei casi, a generare una traccia di stack per ogni messaggio di registro. Probabilmente potresti usare lo stesso metodo per inizializzare in un costruttore statico come hai mostrato, ed evitare quella spesa per messaggio. – Mark

+0

Mark, IMHO log4net * è * in realtà nient'altro che un wrapper - un wrapper attorno a un'implementazione predefinita fornita nella libreria, che puoi scegliere di sostituire da sola ogni volta che vuoi. – chiccodoro

3

usiamo qualcosa quasi identico:

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

Questo metodo è implementato come segue:

[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 { } 

Si dimostra ancora di essere più di un dolore che io sono disposto a passare attraverso. Preferisco di gran lunga un oggetto statico con metodi statici per la registrazione. Ottenere il metodo di chiamata non è così costoso purché non si ottengano informazioni sui file. Rispetto alla sola chiamata di Log4Net per ottenere il tipo di chiamata è nulla (a seconda dei logger utilizzati).

+0

Interessante! Sono decisamente tentato di fare qualcosa del genere ora. – Ken

Problemi correlati