2012-02-07 92 views
5

.net不允許在基類中實現部分接口。作爲一種緩解措施,我已經選擇了3種備選解決方案。請幫助我確定哪一個更適用於重構,編譯/運行時錯誤,可讀性。 但首先有幾條評論。C#在基類/抽象類中的最佳部分接口實現

  • 當然,您可能總是將對象投射到IFoo並在沒有任何編譯器警告的情況下調用任何方法。但這不合邏輯,你不會那樣做。這個構造不會因重構而發生。
  • 我想要最大的分離度。直接類合同(公共方法和屬性)應該與接口實現分開。我使用很多接口來分離對象的交互。

我比較:

  1. BaseClass1/MyClass1的:
    • CON:有在BaseClass1創建虛擬抽象的IFoo的每個未實現的方法。
    • con:其他方法包裝 - 運行時對生產力的影響很小。
  2. BaseClass2/MyClass2:
    • CON:沒有編譯器警告,如果在MyClass2沒有實現方法2的。運行時異常。用單元測試覆蓋較差的重構可能會破壞代碼的穩定性。
    • con:必須放置額外的過時構造來防止子類中的直接方法調用。
    • con:Method2對於BaseClass1是公開的,所以它現在是類合同的一部分。必須放置「過時」構造以防止直接呼叫,而不是通過IFoo。
  3. BaseClass3/MyClass3:
    • 親:(相較於#2)。更可讀。你會發現MyClass2.Method2是IFoo的實現,而不僅僅是一些重載方法。
public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 
public abstract class BaseClass1 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     IFooMethod2(); 
    } 

    protected abstract void IFooMethod2(); 
} 

public class MyClass1 : BaseClass1 
{ 
    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    protected override void IFooMethod2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass2 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    public virtual void Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass2 : BaseClass2 
{ 
    public override void Method2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass3 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass3 : BaseClass3, IFoo 
{ 
    void IFoo.Method2() 
    { 
     //some implementation 
    } 
} 
+6

這是一個**非常尷尬的模式,你試圖實現。你說*「.NET不允許在基類中實現部分接口。」* - 這是有原因的。客戶端代碼需要某種**實現接口**,或許...... **實現接口**。對於不支持的方法拋出異常,當然是*非常*糟糕的代碼異味... – Yuck 2012-02-07 12:47:47

+0

同意Yuck。如果你有一個'IFoo'類型的變量,你真的希望'IFoo'的所有方法都可以實現並且可用。接口是爲了這個。 – ken2k 2012-02-07 12:51:32

+0

只有MyClass1 _必須完全實現接口。它確實如此。問題是有多個子類(我沒有提及它),每個都必須實現IFoo。如果沒有基類,您必須複製/粘貼Method1實現,這對所有子類都是相同的。這是我想要避免的。但是Method2實現在子類中是不同的,所以我不能只有一個實現Method1和Method2的類。 – user1194528 2012-02-07 13:11:56

回答

5

好吧,你可以試試下面的BaseClass是抽象:

public interface IFoo 
{ 
    void Method1(); 

    void Method2(); 
} 

public abstract class BaseClass : IFoo 
{ 
    public void Method1() 
    { 
     // Common stuff for all BaseClassX classes 
    } 

    // Abstract method: it ensures IFoo is fully implemented 
    // by all classes that inherit from BaseClass, but doesn't provide 
    // any implementation right here. 
    public abstract void Method2(); 
} 

public class MyClass1 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass1 
     Console.WriteLine("Class1"); 
    } 
} 

public class MyClass2 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass2 
     Console.WriteLine("Class2"); 
    } 
} 

private static void Main(string[] args) 
{ 
    IFoo test1 = new MyClass1(); 
    IFoo test2 = new MyClass2(); 

    test1.Method2(); 
    test2.Method2(); 

    Console.ReadKey(); 
} 
+0

這是我的變種#2,除了第一個缺點。仍然適用:1.必須添加額外的過時構造以防止來自子類的直接方法調用。在你的情況下,沒有Obsolete,所以直接調用子類不會產生編譯時警告。2. Method2是BaseClass1的公開方法,因此它現在是類合同的一部分。必須放置「過時」構造以防止直接呼叫,而不是通過IFoo。 3.可讀性較差。你沒有看到Method2是IFoo的實現。 – user1194528 2012-02-07 13:38:01

+0

閱讀問題下面的評論,這似乎是解決問題的正確方法。在'BaseClass'中抽象實現'Method2()'確保'IFoo'完全實現,同時迫使所有從'BaseClass'派生的類實現'Method2()'。還可以補充說,派生類將無法重寫'Method1()'(但它們將能夠隱藏它(使用'new'關鍵字))。 – Nailuj 2012-02-07 13:41:04

+0

@ user1194528上述代碼的實際問題是什麼?你爲什麼要放棄已過時的註釋? – ken2k 2012-02-07 13:43:09

6

這是非常糟糕的設計是沒有按類沒有一個明確的定義d合同。這是極端的,因爲你首先說一個班級有能力做某事。你明確地強調類可以做的東西,但後來在你說的那個代碼中,擰緊它,這個類可以在沒有實現的情況下生存。編譯器非常明智地要求您執行合同,但由您決定。

下面是一些常見的解決方案

不好解決

  • 拋出一個異常(NonImplementedException或NotSupportedException異常,見sample
  • 聲明爲過時的(設計它從一開始就很好)

更好解決方案

  • 顯式接口實現,但你仍然實現它(只是一種隱藏)

最好的解決方案

  • 使用接口隔離(拆你的脂肪接口轉換成更薄和更易於管理的)
+1

我想到了界面分離,但問題是界面設計來自客戶端類需求,而不是服務器類。客戶端類不想知道實現細節。爲什麼它應該知道,如果這個接口是在基類中實現的,還有其他的在兒童中呢?它也非常脆弱,可能有兩個類層次結構實現了這個接口,它們在實現在基類和子類之間的分離方式上有所不同。 – user1194528 2012-02-07 14:13:37

0

我建議具有抽象基類實現接口調用protected abstract方式方法,如在第一個例子中,除了一些派生類可能無法實現方法(繼「拋開一切到IList但沒有所有的方法實際上工作「模式);那些可能是protected virtual存根扔NotSupportedException

注意,它是由子類是否對接口的任何特定成員公開爲名稱相同的公共成員(可調用相應的抽象成員)。

在VB.net中的正確模式將類似MustOverride Sub IFoo_Method1() Implements IFoo.Method1,這將避免額外的函數調用開銷,但C#沒有提供任何實現與受保護成員接口的方法。對於可能必須在子類中重寫的任何方法使用顯式接口實現有點難以實現,因爲孩子的接口的重新實現不可能鏈接到父實現。

+0

>>孩子重新實現接口鏈接父母的實現是不可能的。 - 好點子。從這個角度來看,最好將所有方法聲明爲受保護的(+爲那些抽象,未實現)(ken2k變體)。謝謝你們所有的想法。現在我可以做出更明智的決定。 – user1194528 2012-02-08 17:37:08

8

我喜歡這個版本,基類不能被實例化,因爲它的抽象,派生類必須列出的IFoo在其聲明中,否則它將不會被實現接口,然後是實施其餘全權負責的界面。 一個缺點我能看到的是你不能明確地實現在基類的接口方法(即沒有的IFoo:方法),但除此之外,這是一個相當低開銷的版本。

public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 

public abstract class BaseClass1 
{ 
    public void Method1() 
    { 
     //some implementation 
    } 
} 

public class MyClass1 : BaseClass1, IFoo 
{ 
    public void Method2() 
    { 
     //some implementation 
    } 
}