2012-05-22 10 views
5

Digamos que tengo una interfaz, como esto:Uso de métodos de extensión dentro de clase extendida en sí

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

Y algunos métodos de extensión, así:

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... 
} 

Luego, en cualquier class CoreService : ServiceBase, ILoggable o cualquier implemento que public void Log(Func<string> message, Logger.Type type) a lo que me gusta (el modificador público es algo así como meh ...) y utiliza todos los métodos de extensión para hacer el registro real.

Hasta ahora todo bien ... ¿o no tan bueno? ¿Hay algo mal con este enfoque? Si no es así, entonces ¿por qué los inconvenientes:

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

También puede llamar a 'Log (this, ex)' que en este caso parece ser más legible. –

+0

@HenkHolterman: Bueno, tendrías que llamar a Logger.Log (esto, ex) ... –

Respuesta

3

Parece que un enfoque razonable para mí en sí mismo - pero el requisito de indicar explícitamente this es sólo una parte de cómo el lenguaje funciona en torno a los métodos de extensión. Sospecho que esto hace que los aspectos de la especificación del lenguaje sean más limpios, y este requisito es lo suficientemente raro (y la solución es bastante fácil) que se consideró mejor ir con la solución actual que complicar las cosas solo para evitar cinco caracteres en una escenario relativamente raro.

La búsqueda de miembros para nombres simples (sección 7.6.2 de la especificación C# 4) es lo suficientemente complicada sin empeorarla. No olvide que el nombre simple puede referirse a un tipo o un parámetro de tipo, así como a un método. Ya están sucediendo muchas cosas allí.

Cuando empiece a trabajar comprobaré si hay anotaciones en la sección 7.6.5.2 (invocación de método de extensión) que dan "información interna" sobre esto.


En la reflexión, parece un poco extraño para la entidad haciendo que el registro se también desea registrar otras cosas - el único tipo de excepción que había que esperar a ver sería cuando el el registro está fallando, en cuyo caso el registro de la excepción también fallará.

+0

CoreService está principalmente obteniendo datos a través de la red y poniéndolos en la base de datos. Debe tener algunos medios para registrar su actividad. Si algunas de sus operaciones fallan, lo registra. Si el registro falla, puede ignorarlo (para el registro de red) o apagarlo (para el registro de archivos) o lo que sea necesario - eso se implementará al implementar la interfaz. –

+0

@EugeneRyabtsev: Parece que * debe * tener un miembro de registro que sepa cómo hacer el registro, en lugar de implementar una interfaz de registro. Es un detalle de implementación, no algo que debería anunciarse como una capacidad que otros pueden usar, para lo cual son las interfaces. Separe las preocupaciones de iniciar sesión de "obtener datos a través de la red". Escriba una clase cuyo * sole * trabajo es el registro, y luego haga que una instancia de esa clase esté disponible para CoreService. –

+0

Entonces ese miembro, invocable como logger.Log (...), debe ser una clase polimórfica para reutilizar todo el código de registro (...) y debo hacer un descendiente para cada caso particular de lo que quiero iniciar sesión y cómo filtrar encendido/apagado (o hacerlo bastante grande y genérico en primer lugar). Posible, por supuesto. Previamente, implementé este tipo de cosas como herencia múltiple en C++ para que una clase pudiera implementar y registrar su actividad y ser bastante autónomo, las interfaces de pensamiento están lo más cerca posible. –

Cuestiones relacionadas