2010-06-07 103 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; } } 
} 
相关问题