2011-03-09 13 views
6

Estoy tratando de deshacerme de las directivas "#if TRACE" en mi código, utilizando el atributo Conditional en su lugar, pero no puedo aplicar este enfoque fácilmente a las interfaces. Tengo una forma de evitar esto, pero es bastante feo, y estoy buscando una mejor solución.Atributo condicional de C# en el miembro de interfaz

E.g. Tengo una interfaz con un método compilado condicionalmente.

interface IFoo 
{ 
#if TRACE 
    void DoIt(); 
#endif 
} 

no puedo usar el atributo condicional en una interfaz:

// Won't compile. 
interface IFoo 
{ 
    [Conditional("TRACE")] 
    void DoIt(); 
} 

que podría tener el método de interfaz simplemente llamar a un método privado condicional en la clase concreta:

interface IFoo 
{ 
    void TraceOnlyDoIt(); 
} 

class Foo : IFoo 
{ 
    public void TraceOnlyDoIt() 
    { 
     DoIt(); 
    } 

    [Conditional("TRACE")] 
    void DoIt() 
    { 
     Console.WriteLine("Did it."); 
    } 
} 

Esto dejaría mi código de cliente con llamadas redundantes al método 'nop' TraceOnlyDoIt() en una compilación que no sea TRACE. Puedo solucionarlo con un método de extensión condicional en la interfaz, pero se está poniendo un poco feo.

interface IFoo 
{ 
    void TraceOnlyDoIt(); 
} 

class Foo : IFoo 
{ 
    public void TraceOnlyDoIt() 
    { 
     Console.WriteLine("Did it."); 
    } 
} 

static class FooExtensions 
{ 
    [Conditional("TRACE")] 
    public static void DoIt(this IFoo foo) 
    { 
     foo.TraceOnlyDoIt(); 
    } 
} 

¿Hay una manera mejor de hacer esto?

+0

¿Sería útil usar [métodos parciales] (http://msdn.microsoft.com/en-us/library/wa80x488.aspx) aquí? –

+5

Siento que si intentas hacer esto con las interfaces, es posible que tengas una abstracción con goteras en alguna parte. La información de rastreo puede ser más un detalle de implementación que un detalle de contrato. – R0MANARMY

+0

@Jeff M: ¿No ves cómo? – Ergwun

Respuesta

4

No debe aparecer un método de rastreo en una interfaz, ya que es un detalle de implementación.

Pero si está atrapado en la interfaz y no puede cambiarla, entonces usaría el enfoque #if ... #endif que comenzó.

Es una sintaxis más salvaje sin embargo, así que simpatizan con qué es posible que desee evitar que ...

1

Qué tal esto:

interface IFoo 
{ 
    // no trace here 
} 

class FooBase : IFoo 
{ 
#if TRACE 
    public abstract void DoIt(); 
#endif 
} 

class Foo : FooBase 
{ 
#if TRACE 
    public override void DoIt() { /* do something */ } 
#endif 
} 
+1

Pero ahora tiene que cambiar todas las referencias a IFoo a FooBase para usar su método Condicional ... – sheikhjabootie

1

Yo sugeriría que utilice el null object pattern lugar. Veo las declaraciones condicionales como una especie de olor a código porque ocultan abstracciones reales. Sí, obtendrá algunas llamadas a métodos adicionales, pero éstas prácticamente no tienen impacto en el rendimiento. En las construcciones de trazas puede inyectar el TraceFoo, a través de un archivo de configuración, por ejemplo. Esto también le dará la capacidad de poder habilitarlo en compilaciones que no sean de rastreo.

interface IFoo 
{ 
    void DoIt(); 
} 

class NullFoo : IFoo 
{ 
    public void DoIt() 
    { 
     // do nothing 
    } 
} 

class TraceFoo : IFoo 
{ 
    public void DoIt() 
    { 
     Console.WriteLine("Did it."); 
    } 
} 
+1

Supongo que el IFoo del OP en realidad tiene algunos comportamientos distintos del método DoIt que desea permanecer en la interfaz. En ese caso, pasar un "objeto nulo" en lugar de la clase concreta perdería parte del comportamiento que quería mantener. – sheikhjabootie

0

Debe dejar la tarea para el optimizador (o compilador JIT) y uso:

interface IWhatever 
{ 
    void Trace(string strWhatever); 
} 

class CWhatever : IWhatever 
{ 
    public void Trace(string strWhatever) 
    { 
#if TRACE 
     // Your trace code goes here 
#endif 
    } 
} 

Ni el optimizador ni el compilador JIT eliminan la llamada; debe escribir correos electrónicos molestos a los desarrolladores;).

1

Me gusta el método de extensión. Se puede hacer un poco más agradable/robusta, al menos para las personas que llaman:

public interface IFoo 
    { 
     /// <summary> 
     /// Don't call this directly, use DoIt from IFooExt 
     /// </summary> 
     [Obsolete] 
     void DoItInternal(); 
    } 

    public static class IFooExt 
    { 
     [Conditional("TRACE")] 
     public static void DoIt<T>(this T t) where T : IFoo 
     { 
#pragma warning disable 612 
      t.DoItInternal(); 
#pragma warning restore 612 
     } 
    } 

    public class SomeFoo : IFoo 
    { 
     void IFoo.DoItInternal() { } 

     public void Blah() 
     { 
      this.DoIt(); 
      this.DoItInternal(); // Error 
     } 
    } 

limitaciones de tipo genérico se utilizan para evitar la llamada virtual y el boxeo potencial de los tipos de valores: el optimizador debe hacer frente a este pozo. Al menos en Visual Studio genera advertencias si llama a la versión interna a través de Obsoleto. Se usa una implementación de interfaz explícita para evitar llamar accidentalmente a los métodos internos en los tipos concretos: también funciona marcarlos con [Obsoleto].

Si bien esta puede no ser la mejor idea para el material Trace, hay algunos casos en los que este patrón es útil (encontré mi camino aquí desde un caso de uso no relacionado).

Cuestiones relacionadas