2009-04-29 57 views
26

有關C#中內部類的使用和結構的最佳實踐是什麼?在C#中使用內部類#

例如,如果我有一個非常大的基類和兩個大的內部類,我應該將它們拆分成單獨的(部分類)代碼文件還是將它們留作一個非常大的笨拙的代碼文件?

也有一個抽象類,具有公共繼承的內部類是不好的做法?

回答

23

通常我保留內類的兩個目的之一:

  1. 公共類,其從它們的父類派生其中父類是具有一個或多個抽象方法的抽象基本實現與每個子類是一個服務於特定實現的實現。閱讀框架設計和指導後,我看到這個標記爲「避免」,但我用它在類似枚舉的場景 - althogh這可能給人留下不好的印象,以及

  2. 的內部類是私人和是業務邏輯的單元,或者以其他方式消耗或使用時基本上被破壞的方式緊密耦合到其父類。

對於所有其他情況下,我儘量讓他們在相同的命名空間和相同的進入等級爲他們的消費/邏輯父 - 經常與比「主」類有點不太友好的名稱。

在大型項目中,您會驚奇地發現自己最初構建強耦合組件的原因很簡單,因爲它的首要或主要目的似乎是合乎邏輯的 - 但除非您有非常好的或技術上的理由來鎖定它放下並隱藏它看不見,然後暴露類沒有什麼壞處,以便其他組件可以消耗它。

編輯請記住,即使我們在談論子類,他們應該是或多或少精心設計和鬆散耦合的組件。即使它們是私密的,並且在外部世界不可見的情況下,在類之間保持最小的「表面積」也會極大地減少代碼對於未來擴展或更改的可維護性。

9

通常,內部類應該是私有的,只能由包含它們的類使用。如果他們的內部階層非常大,那麼他們應該成爲他們自己的階級。

通常當你有一個大的內部類時,這是因爲內部類與它的類緊密耦合,需要訪問其私有方法。

8

我認爲這是相當主觀的,但我可能會將它們分成不同的代碼文件,使「主機」類成爲部分。

通過這樣做,您可以通過editing the project file獲得更多概述,使文件組與Windows窗體中的設計器類一樣。我想我已經看到了一個Visual Studio插件,它可以自動爲你做這件事,但我不記得在哪裏。

編輯:
經過一番尋找,我發現在Visual Studio插件這樣做的叫VSCommands

+2

難道他們如果他們的名字MyClass的這個自動在Visual Studio中做。 cs,MyClass.subportion.cs,MyClass.anotherbit.cs,MyClass.lastoneipromise.cs等? – 2009-04-29 22:03:44

+0

我個人沒有這樣做,但也許他們已經在Visual Studio 2008中實現了它? – Patrik 2009-04-29 22:06:48

+1

我剛剛測試過,Visual Studio不會自動執行此操作。 – Patrik 2009-04-29 22:10:12

18

我沒有書的手,但框架設計指南建議使用public內部類只要客戶不必參考類名即可。內部類是好的:沒有人會注意到這些。

壞:ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();

好:listView.Items.Add(...);

關於你大類:它通常是值得的分裂像這樣成更小的類別,每一個特定的功能塊。開始時很難分解它,但我預測它會讓你的生活更輕鬆...

3

我個人喜歡每個文件有一個類,內部類是該文件的一部分。我相信內部類通常(幾乎總是)是私有的,並且是類的實現細節。讓他們在一個單獨的文件混淆事情,國際海事組織。

使用代碼區域來環繞內部類並隱藏它們的細節對於我來說很好,在這種情況下,並且使得文件難以處理。代碼區域保持內部類爲「隱藏」,並且由於它是私人實現細節,所以對我來說沒什麼問題。

2

我個人使用內部類來封裝一些只在內部使用的概念和操作。這樣我就不會污染該類的非公開API,並保持API清潔和緊湊。

您可以利用部分類將這些內部類的定義移動到不同的文件中,以便更好地組織。除了一些模板化的項目,例如ASP.NET,WinForm窗體等,VS不會爲您自動分組部分類文件。您需要編輯項目文件並在其中進行一些更改。您可以查看其中的一個現有分組,以瞭解它是如何完成的。我相信有一些宏允許您在解決方案資源管理器中爲您分組部分類文件。

6

對於只有如何構建這樣的野獸......

您可以使用部分類分裂主類和嵌套類。當你這樣做時,建議你適當地命名文件,這很明顯是怎麼回事。

// main class in file Outer.cs 
namespace Demo 
{ 
    public partial class Outer 
    { 
    // Outer class 
    } 
} 

// nested class in file Outer.Nested1.cs 
namespace Demo 
{ 
    public partial class Outer 
    { 
    private class Nested1 
    { 
     // Nested1 details 
    } 
    } 
} 

以幾乎相同的方式,您經常會在他們自己的文件中看到(顯式)接口。例如Outer.ISomeInterface.cs而不是編輯器默認的#region

你的項目文件結構,然後開始看起來像

 
    /Project/Demo/ISomeInterface.cs 
    /Project/Demo/Outer.cs 
    /Project/Demo/Outer.Nested1.cs 
    /Project/Demo/Outer.ISomeInterface.cs 

通常,當我們正在做這個是爲了在Builder模式的變體。

0

在我看來,如果需要的話,內部類應該保持較小,並且只能在該類內部使用。如果你在.NET框架上使用Relfector,你會發現它們僅僅用於這個目的。

如果你的內部類變得太大,我肯定會將它們移出單獨的類/代碼文件,只是爲了可維護性。我必須支持一些現有的代碼,其中有人認爲在內部類中使用內部類是一個好主意。它導致內部類層次結構運行4 - 5級。毋庸置疑,代碼是難以穿透的,需要很長時間才能理解您所看到的內容。

0

這裏看到一個嵌套類,可以給你他們使用的想法的一個實際的例子(添加了一些單元測試)

namespace CoreLib.Helpers 
{ 
    using System; 
    using System.Security.Cryptography; 

    public static class Rnd 
    { 
     private static readonly Random _random = new Random(); 

     public static Random Generator { get { return _random; } } 

     static Rnd() 
     { 
     } 

     public static class Crypto 
     { 
      private static readonly RandomNumberGenerator _highRandom = RandomNumberGenerator.Create(); 

      public static RandomNumberGenerator Generator { get { return _highRandom; } } 

      static Crypto() 
      { 
      } 

     } 

     public static UInt32 Next(this RandomNumberGenerator value) 
     { 
      var bytes = new byte[4]; 
      value.GetBytes(bytes); 

      return BitConverter.ToUInt32(bytes, 0); 
     } 
    } 
} 

[TestMethod] 
public void Rnd_OnGenerator_UniqueRandomSequence() 
{ 
    var rdn1 = Rnd.Generator; 
    var rdn2 = Rnd.Generator; 
    var list = new List<Int32>(); 
    var tasks = new Task[10]; 
    for (var i = 0; i < 10; i++) 
    { 
     tasks[i] = Task.Factory.StartNew((() => 
     { 
      for (var k = 0; k < 1000; k++) 
      { 
       lock (list) 
       { 
        list.Add(Rnd.Generator.Next(Int32.MinValue, Int32.MaxValue)); 
       } 
      } 
     })); 
    } 
    Task.WaitAll(tasks); 
    var distinct = list.Distinct().ToList(); 
    Assert.AreSame(rdn1, rdn2); 
    Assert.AreEqual(10000, list.Count); 
    Assert.AreEqual(list.Count, distinct.Count); 
} 

[TestMethod] 
public void Rnd_OnCryptoGenerator_UniqueRandomSequence() 
{ 
    var rdn1 = Rnd.Crypto.Generator; 
    var rdn2 = Rnd.Crypto.Generator; 
    var list = new ConcurrentQueue<UInt32>(); 
    var tasks = new Task[10]; 
    for (var i = 0; i < 10; i++) 
    { 
     tasks[i] = Task.Factory.StartNew((() => 
     { 
      for (var k = 0; k < 1000; k++) 
      { 
        list.Enqueue(Rnd.Crypto.Generator.Next()); 
      } 
     })); 
    } 
    Task.WaitAll(tasks); 
    var distinct = list.Distinct().ToList(); 
    Assert.AreSame(rdn1, rdn2); 
    Assert.AreEqual(10000, list.Count); 
    Assert.AreEqual(list.Count, distinct.Count); 
}