2010-09-21 72 views
1

我建設,我已經有了喜歡的圖案庫中的以下內容:尋找一種方法來隱藏類的基類

public class Foo : Foo_Impl 
{ 
} 

public class Foo_Impl 
{ 
} 

我不希望其他開發商不小心使用Foo_Impl類。我有什麼選擇可以隱藏這個選項?我也想從同一裝配它的定義中的其他類隱藏在理想情況下我會喜歡做:

public class Foo : Foo_Impl 
{ 
    private class Foo_Impl 
    { 
    } 
} 

但是,這並不適用於各種原因的工作。

回答

5

Foo_Impl一個抽象類。這不會阻止其他開發人員從中派生出來,但它將無法直接創建Foo_Impl的實例 - 它需要通過創建派生對象(例如Foo)來實例化。

public abstract class Foo_Impl 
{ 
    public Foo_Impl() 
    { 
    } 
} 

public class Foo : Foo_Impl 
{ 
    public Foo() // implicitly calls the base class's constructor 
    { 
    } 
} 

-

var works = new Foo(); // Works 
var error = new Foo_Impl(); // Compiler error 

每托馬斯·萊維斯克的建議,還具有使抽象構造內部的選項:

public abstract class Foo_Impl 
{ 
    internal Foo_Impl() 
    { 
    } 
} 

public class Foo : Foo_Impl 
{ 
    public Foo() // implicitly calls the base class's constructor 
    { 
    } 
} 

這將防止開發商從Foo_Impl來自外部的繼承部件。

+5

您*可以*阻止其他開發人員從其派生出來:只是將構造函數內部化爲 – 2010-09-21 23:32:47

+0

這是防止人們直接實例化Foo_Impl()的最合適的語義。 – Michael 2010-09-22 01:38:07

+0

@ Thomas-Levesque我在同一個議會中想,但我想我應該提到那個選項。 – Jake 2010-09-22 05:10:16

4

無法完全隱藏它。如果Foo將要公開,其基類也必須公開。但是,通過使構造函數受到保護,可以防止其他開發人員創建Foo_Impl的實例。這樣構造函數只能從派生類中使用。

public class Foo_Impl 
{ 
    protected Foo_Impl() 
    { 
    } 
} 

public class Foo : Foo_Impl 
{ 
    public Foo() // implicitly calls the base class's protected constructor 
    { 
    } 
} 

如果你不希望人們能夠創建Foo_Impl派生自己的類,使構造內部

4

你的選項是:

  • 讓Foo_Impl抽象。開發人員將無法直接實例化Foo_Impl;他們必須聲明一個Foo。但是,Foo_Impl仍然可以用作方法參數或泛型類型參數,開發人員可以從Foo_Impl派生自己的類。

  • 製作Foo_Impl內部,並將它和Foo放置在您想要隱藏Foo_Impl的任何其他類的獨立庫中。在該程序集外工作的開發人員將無法看到Foo_Impl。但是,如果開發人員在運行時擁有足夠的CAS權限,則可以反映該類型並實例化一個類型,並且任何有權訪問程序集源的開發人員都可以在該程序集中創建一個Foo_Impl。

  • 使Foo_Impl的所有構造函數受保護或內部。受保護的構造函數只能由派生類訪問,而內部構造函數只能在同一個程序集中訪問。無論哪種方式,Foo_Impl都不能由外人實例化。但是,開發人員仍然可以將該類用作參數或泛型類型。

  • 使用[Obsolete]屬性將Mark Foo_Impl的構造函數或整個類廢棄爲Obsolete,指定使用此類應引發錯誤。這有點破解,但你可以防止編譯「錯誤」的代碼,並使用屬性的Message參數來指定應該使用什麼。但是,小心你裝飾什麼;如果你裝飾整個類,我不認爲編譯器會讓你對類做任何事情,包括繼承它。裝飾構造函數應該阻止大多數編碼器。

+0

+1非常徹底。只是好奇最後一個項目...那不會阻止Foo使用Foo_Impl嗎? – harpo 2010-09-21 23:27:14

+3

你的第二點是錯誤的:如果你讓Foo_Impl內部,那麼Foo不能公開。否則,非常好的答案;) – 2010-09-21 23:34:31

9

我還沒有看到這個建議,所以我想我會加我的.02。

如何使用組合而不是繼承? Foo_Impl的一個實例可以由Foo維護,並且永遠不會被外部世界看到(因爲Foo_impl對於程序集來說是私有的)。調用可以根據需要通過接口傳遞給Foo_Impl函數。您可以獲得相同的功能,而且不會產生任何設計問題。至於「從程序集中的其他類中隱藏它」,如果你真的覺得這是合適的話,那麼你可以把它作爲一個嵌套類。

+0

我欣賞這個建議,這是我第一次看到問題時給出的建議,但在這種情況下,我特別需要繼承 – Hounshell 2010-09-22 18:05:08

+0

爲什麼?也許你會這樣做,或者你被精神上鎖定在一個可能不是最佳選擇的單一解決方案中。 – 2010-09-22 18:34:30

+0

我正在生成一些代碼。最終我希望用戶使用A類,而A_Impl提供實現。該實現的一些部分是存根,以便用戶生成的代碼可以由其他事件觸發。這種經典模型就是MS用C#WinForms所做的事情:爲事前/事後行爲提供事件,但這意味着公開許多受保護的成員,處理多播場景以及記錄線程模型。最終,我想避開事件的混亂,並提供更高級別的封裝。 – Hounshell 2010-09-22 22:04:30

3

這聽起來很愚蠢,但有一個非常簡單的解決方案:記錄正確的用法。如果Foo_Impl不應該被實例化,那麼給這個類添加一個註釋以說明這一點。將其包含在有關該課程的任何其他文檔中。

即使您在此實施其他解決方案之一,也可以(也應該)這樣做。

+0

+1尤其是因爲它讓你有機會陳述**爲什麼**不應該被實例化。這可能是更有趣和信息。 – 2010-09-22 06:00:19

0

好吧,我想我已經解決了謎題。您可以在兩個部分中實現Foo_Impl。

其中一個是public partial class Foo_Impl {}實現,它基本上是一個空類,什麼都不做,所以它在任何意義上都是不可用的。

另一個實現爲Foo:Foo_Impl類的private partial class Foo_Impl{}成員。那就是你放置了所有你的祕密功能,只有Foo才能使用它。任何類都可以繼承Foo_Impl的部分公共部分,但不能使用Foo通過其私有的Foo_Impl標記唯一可用的任何專用功能。例如class Voyeur:Foo_Impl用於說明每個主體都可以繼承,但只有Foo可以實例化並使用它。 Voyeur不能窺視到Foo_Impl以窺見其有用的私人部分;)。

using System; 



    static class Program 
    { 


     static void Main(string[] args) 
     { 

      new Foo(); 
      new Voyeur(); 


      Console.ReadLine(); 

     } 


    } 


    public partial class Foo_Impl 
    { 
     // NOTHING in this public partial implementation. 
     // Visible but cannot be used for anything 

     protected Foo_Impl() 
     { 
      Console.Write("public partial Foo_Impl's protected constructor "); 
     } 

    } 



    public class Foo : Foo_Impl 
    { 
     private partial class Foo_Impl { 

      // put all your secret implementations here that you dont want 
      // any other class to accidentally use (except Foo) 

      public int fooAccessibleVariable=42; 

      public Foo_Impl() 

      { 
       Console.Write("private partial Foo_Impl contructor "); 

      } 



     } 

     public Foo():base() // calls the public partial Foo_Impl's construtor 
     { 
      Console.WriteLine(" called from Foo()"); 


      Foo_Impl myFoo_Impl = new Foo_Impl(); // calls the private partial Foo_Imp's constructor 
      Console.WriteLine(" called from Foo()"); 

      Console.WriteLine("private Foo_Impl's variabe thats only accessible to Foo: {0}", 
       myFoo_Impl.fooAccessibleVariable); 
     } 

    } 

    class Voyeur:Foo_Impl 
    { 



     public Voyeur():base()// calls the public partial Foo_Impl's contructor 
     { 

      Console.WriteLine(" called from Voyeur()"); 

     } 


    } 
1

抽象關鍵字可能是最適用於您的方案。請記住,如果您提供的程序集(dll)希望人們利用某些特定功能,則可以通過接口實現黑盒子範例。如果您提供要使用的接口並在內部實現該接口,則程序集可以提供接口引用,而不會將引用移交給實例化類本身。所有的開發人員都知道你在界面定義中定義的屬性和方法。這在插件體系結構中非常常見。

相關問題