2011-03-09 80 views
6

我想通過使用條件屬性來代替我的代碼中的「#if TRACE」指令,但不能輕鬆地將這種方法應用於接口。我有辦法解決這個問題,但這非常難看,我正在尋找更好的解決方案。接口成員上的C#條件屬性

E.g.我有一個有條件編譯方法的接口。

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

我不能使用的接口條件屬性:

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

我能有接口方法只需要調用的具體類有條件的私有方法:

interface IFoo 
{ 
    void TraceOnlyDoIt(); 
} 

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

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

這會讓我的客戶代碼在非TRACE構建中多次調用'nop'TraceOnlyDoIt()方法。我可以通過界面上的條件擴展方法得到它,但它變得有點難看。

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(); 
    } 
} 

有沒有更好的方法來做到這一點?

+0

使用[部分方法](http://msdn.microsoft.com/zh-cn/library/wa80x488.aspx)會對你有用嗎? – 2011-03-09 05:40:44

+5

我覺得如果你試圖做到這一點,你可能會在某個地方出現抽象漏洞。跟蹤信息可能比合同細節更多的是實現細節。 – R0MANARMY 2011-03-09 05:47:01

+0

@Jeff M:看不見怎麼樣? – Ergwun 2011-03-09 05:58:09

回答

4

跟蹤方法不應該出現在接口上,因爲它是一個實現細節。

但是,如果您遇到了界面,並且無法更改它,那麼我會使用您開始使用的#if ... #endif方法。

這是一個相當野蠻的語法雖然,所以我爲什麼你可能要避免它同情......

1

這個怎麼樣:

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

但是現在他必須將IFoo的所有引用都更改爲FooBase才能使用他的條件方法... – sheikhjabootie 2011-03-09 10:02:43

1

我會建議你使用null object pattern代替。我將條件語句視爲一種代碼嗅覺,因爲它們隱藏真正的抽象。是的,你會得到一些額外的方法調用,但這些對性能幾乎沒有影響。在跟蹤構建中,您可以通過配置文件注入TraceFoo,例如。這也將使您能夠在非跟蹤構建中啓用它。

interface IFoo 
{ 
    void DoIt(); 
} 

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

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

我猜測OP的IFoo實際上具有他希望保留在界面上的DoIt方法以外的一些行爲。在這種情況下,傳遞一個「空對象」而不是具體的類會失去他想要保留的一些行爲。 – sheikhjabootie 2011-03-09 10:09:18

0

你應該離開任務優化器(或JIT編譯器)和使用:

interface IWhatever 
{ 
    void Trace(string strWhatever); 
} 

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

優化程序和JIT編譯器都不會刪除調用,您應該向這些開發人員寫出生氣的電子郵件;)。

1

我喜歡擴展方法的方法。它可以做更好一點/健壯,至少呼叫者:

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

泛型類型約束來避免值類型的虛擬呼叫和潛在拳:優化程序處理這口井。至少在Visual Studio中,如果您通過Obsolete調用內部版本,則會生成警告。明確的接口實現用於防止在具體類型上意外地調用內部方法:使用[Obsolete]標記它們也可以。

雖然這可能不是Trace東西的最佳主意,但有些情況下這種模式很有用(我從一個不相關的用例中找到了我的方式)。