2010-06-07 105 views
6

我有,一般會從數據構建的Stream對象的小的分層次,但對於一些特定的子類,可以從一個簡單的參數列表來合成。在從子類鏈接構造函數時,我遇到了確保處理基類構造函數所需的合成流的問題。它不逃避我這種方式使用IDisposable對象可能只是骯髒的池(PLZ建議?)由於我沒有考慮的原因,但除了這個問題,它似乎相當簡單(和良好的封裝)。傳遞IDisposable的對象,通過構造函數鏈

代碼:

abstract class Node { 
    protected Node (Stream raw) 
    { 
     // calculate/generate some base class properties 
    } 
} 
class FilesystemNode : Node { 
    public FilesystemNode (FileStream fs) 
     : base (fs) 
    { 
     // all good here; disposing of fs not our responsibility 
    } 
} 
class CompositeNode : Node { 
    public CompositeNode (IEnumerable some_stuff) 
     : base (GenerateRaw (some_stuff)) 
    { 
     // rogue stream from GenerateRaw now loose in the wild! 
    } 

    static Stream GenerateRaw (IEnumerable some_stuff) 
    { 
     var content = new MemoryStream(); 
     // molest elements of some_stuff into proper format, write to stream 
     content.Seek (0, SeekOrigin.Begin); 
     return content; 
    } 
} 

我認識到,沒有設置一MemoryStream的是不完全的壞CLR公民的世界停止的情況下,但它仍然讓我神經過敏(更不用提,我可能並不總是使用其他子類型的MemoryStream)。這不是在範圍上,所以我不能明確地Dispose()後來在構造函數中,並加入在GenerateRaw()一個using語句是弄巧成拙,因爲我需要的流返回。

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

先發制人:

  • 是,在Node構造計算的性質應該是基類的一部分,不應該由(或接近)來計算的子​​類
  • 我不會要求流被傳遞到CompositeNode(其格式應該無關的呼叫者)
  • 先前迭代不得不在基類中的值計算作爲一個單獨的受保護的方法,該方法我那麼就稱爲在每個亞型的構造的端,將GenerateRaw()的主體轉化爲一個使用CompositeNode構造函數體中的語句。但要求呼籲每個構造,不能夠的重複,以保證它的每一個亞型永遠運行(一Node不是Node,語義,如果沒有這些屬性進行初始化)給了我神經過敏遠遠高於糟糕(潛力)資源泄漏在這裏呢。
+0

我知道這是一個老問題,但如果你還記得。 。爲什麼你沒有在構造函數參數中使用字節數組而不是流? – Oscar 2017-10-18 22:11:04

回答

4

CompositeNode創建的流 - CompositeNode有責任,除非一些其他的代碼明確指出它會採取上。這裏的一個選擇是基礎構造函數通過受保護的重載來允許這個選項,但排序意味着很難確切地知道何時可以安全地處理它。一個virtual方法可以讓你做到這一點,但你不應該真的在構造函數中調用virtual方法。

我不知道是否重構,以便有一個初始化Load)方法(你分開呼叫建設)會更好。也許一個protected virtual方法,並通過public static方法公開它。

+0

重載基構造器的絕妙想法。由於我沒有在子類構造函數中使用Stream,所以我認爲它很安全,當基礎構造函數完成運行時,一切都很好。現在使用布爾型參數重載原型,似乎是一個非常簡單的解決方案,即正確的事情。 – 2010-06-07 11:45:20

-1

主要的經驗法則是,創建一次性對象的實例的代碼應該處置它。如果你有一個IDisposable對象傳入一個方法,你應該使用它來滿足你的需要,並且保持獨立。

的一個好方法,以確保你總是這樣做是使用using ([IDisposable object]) { ... }模式,這將調用自動處置對象上完成的範圍時。

+0

沒錯,但是如果你看一下示例代碼,就會在(鏈接的)CompositeNode構造函數中創建流。如何處理這是我的問題。 ;) – 2010-06-07 11:33:01

+0

當CompositeNode被丟棄? – 2010-06-07 11:36:34

+0

似乎過度殺傷;我明確沒有在任何地方持有對Stream的引用,並且CompositeNode不需要是IDisposable。 – 2010-06-07 11:41:29

2

您可能希望考慮將處理指令作爲/接受IDisposable的構造函數的單獨參數傳遞。這是XmlReader.Create採用的方法,它接受一個XmlReaderSettings參數,其CloseInput屬性確定在創建的XmlReader最終處置時是否放置基礎數據源。

+0

Marc自從他第一次就得到了接受的答案,但爲了「正確」的方法而贊成,因爲這是我最終做的。謝謝! – 2010-06-07 13:20:57

0

對於這個簡單的例子,我會用一個InitializeFrom(Stream s)方法:

abstract class Node 
{ 
    public Node(Stream stream) { InitializeFrom(stream); } 
    protected Node() { } 
    protected void InitializeFrom(Stream stream); 
} 

class FilesystemNode 
{ 
    public FilesystemNode(FileStream stream) : base(stream) {} 
} 

class CompositeNode 
{ 
    public CompositeNode(IEnumerable values) : base() 
    { 
     using (var stream = new MemoryStream()) 
     { 
      // init stream 
      InitializeFrom(stream); 
     } 
    } 
} 

使其成爲虛擬的,如果你有一個更深的層次。我傾向於發現這樣的代碼有點難以跟蹤,並且使用我在整個庫/框架代碼中看到的模式:分解爲普通對象(最好是不可變的,並且不知道是什麼創建它們,例如僅從它們的成員)與讀者(或工廠如果數據不是從流來)創建它們,而是一箇中間地帶是一個靜態讀寫方法:

abstract class Node 
{ 
    NodeKind kind; 
    public Node(NodeKind kind) { this.kind = kind; } 
    public NodeKind Kind { get { return kind; } } 

    static Node ReadFrom(Stream stream); 
} 

class FilesystemNode : Node 
{ 
    string filename; 
    public FilesystemNode(string filename) : Node(NodeKind.Filesystem) 
    { 
     this.filename = filename; 
    } 
    public string Filename { get { return filename; } } 

    static FilesystemNode ReadFrom(FileStream stream); 
} 

class CompositeNode : Node 
{ 
    Node[] values; 
    // I'm assuming IEnumerable<Node> here, but you can store whatever. 
    public CompositeNode(IEnumerable<Node> values) : Node(NodeKind.Composite) 
    { 
     this.values = values.ToArray(); 
    } 
    public IEnumerable<Node> { get { return filename; } } 
} 
相關問題