2012-05-22 11 views
5

Diciamo che ho un'interfaccia, in questo modo:Utilizzando metodi di estensione all'interno classe estesa si

public interface ILoggable 
{ 
    void Log(Func<string> message, Logger.Type type); 
} 

E alcuni Metodi di estensione, come questo:

public static class Logger 
{ 
    public static void Log(this ILoggable loggable, Func<string> message) { loggable.Log(message, Type.Information); } 
    public static void Log(this ILoggable loggable, string prefix, byte[] data, int len) { /* snip */ } 
    public static void Log(this ILoggable loggable, Exception ex) { /* snip */ } 
    // And so on... 
} 

Poi in qualsiasi class CoreService : ServiceBase, ILoggable o tali implemento che public void Log(Func<string> message, Logger.Type type) a qualsiasi cosa mi piaccia (il modificatore pubblico è una specie di meh ...) e utilizzare tutti i metodi di estensione per fare il logging reale.

Fin qui tutto bene ... o non così buono? C'è qualcosa di sbagliato in questo approccio? Se no, allora perché l'inconveniente:

catch (Exception ex) { 
    this.Log(ex); // this works 
    Log(ex); // this goes not 
+0

Puoi anche chiamare 'Log (this, ex)' che sembra del tutto più leggibile in questa situazione. –

+0

@HenkHolterman: Beh, dovresti chiamare Logger.Log (questo, ex) ... –

risposta

3

Sembra un approccio ragionevole per me in sé - ma l'obbligo di dichiarare esplicitamente this è solo una parte come il linguaggio funziona in tutto metodi di estensione. Sospetto che ciò renda gli aspetti delle specifiche della lingua più puliti, e questo requisito sia sufficientemente raro (e la soluzione alternativa è abbastanza facile) che si è ritenuto che fosse meglio seguire la soluzione corrente piuttosto che rendere le cose più complicate solo per evitare cinque caratteri in una scenario relativamente raro.

La ricerca di membri per nomi semplici (sezione 7.6.2 delle specifiche C# 4) è abbastanza complicata senza peggiorarla. Non dimenticare che il nome semplice può fare riferimento a un parametro di tipo o tipo, nonché a un metodo. C'è già molto da fare.

Quando arrivo al lavoro controllerò se ci sono annotazioni intorno alla sezione 7.6.5.2 (chiamata al metodo di estensione) che danno "informazioni interne" a riguardo.


Riflettendoci, sembra un po 'strano per l'entità fare la registrazione per anche desidera accedere le altre cose - l'unico tipo di eccezione che ci si aspetta per vedere sarebbe quando la la registrazione sta fallendo, nel qual caso la registrazione dell'eccezione fallirebbe anche.

+0

CoreService sta principalmente recuperando i dati dalla rete e inserendoli nel database. Deve avere alcuni mezzi per registrare la sua attività. Se alcune delle sue operazioni falliscono, lo registra. Se la registrazione fallisce, potrebbe ignorarlo (per la registrazione di rete) o spegnersi (per la registrazione dei file) o watever - che deve essere implementato quando si implementa l'interfaccia. –

+0

@EugeneRyabtsev: Sembra che dovrebbe * avere * un membro di log che sa come fare il logging, piuttosto che implementare un'interfaccia di logging. È un dettaglio di implementazione - non qualcosa che dovrebbe essere pubblicizzato come una capacità che gli altri possono utilizzare, che è ciò per cui sono le interfacce. Separare le preoccupazioni del logging da "ottenere dati attraverso la rete". Scrivi una classe il cui lavoro * solo * sta registrando, quindi rendi disponibile un'istanza di tale classe a CoreService. –

+0

Quindi quel membro, richiamabile come logger.Log (...), deve essere una classe polimorfica per riutilizzare tutto il codice Log (...) e devo creare un discendente per ogni caso particolare di ciò a cui voglio accedere e come filtrare on/off (o renderlo abbastanza grande e generico in primo luogo). Possibile, ovviamente. Precedentemente ho implementato cose come questa come ereditarietà multipla in C++, in modo che una classe potesse implementare e registrare la sua attività ed essere praticamente autosufficiente, le interfacce pensate sono il più vicino possibile. –

Problemi correlati