通过构造函数链传递IDisposable对象

时间:2010-06-07 11:20:57

标签: c# idisposable constructor

我有一个小的对象层次结构,通常是从Stream中的数据构造的,但是对于某些特定的子类,可以从更简单的参数列表中合成。在从子类链接构造函数时,我遇到了一个问题,即确保处理基类构造函数所需的合成流。它没有逃过我,以这种方式使用IDisposable对象可能只是脏池(plz建议?),原因我没有考虑过,但是,除了这个问题,它似乎相当简单(并且很好的封装)。

代码:

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公民身份不好的案例,但它仍然给了我heebie-jeebies(更不用说我可能并不总是使用{ {1}}用于其他子类型)。它不在范围内,所以我不能在构造函数中稍后显式MemoryStream,并且在Dispose ()中添加using语句会弄巧成拙,因为我需要返回流。

有更好的方法吗?

先发制人的罢工:

  • 是的,在GenerateRaw ()构造函数中计算的属性应该是基类的一部分,不应该由子类计算(或可以在子类中访问)
  • 我不会要求将流传递给CompositeNode(其格式应与调用者无关)
  • 上一次迭代将基类中的值计算作为一个单独的受保护方法,然后我在每个子类型构造函数的末尾调用,将Node的主体移动到主体中的using语句中GenerateRaw ()构造函数。但重复要求调用每个构造函数并且不能保证它为每个子类型运行(CompositeNode不是Node,在语义上,没有初始化这些属性)给了我heebie -jeebies比这里的(潜在)资源泄漏要糟糕得多。

4 个答案:

答案 0 :(得分:4)

CompositeNode创建了流 - CompositeNode有责任,除非其他一些代码明确声明它会接受它。这里的一个选择是基础构造函数通过受保护的重载来允许它,但是排序意味着很难确切地知道何时可以安全地处理它。 virtual方法允许您执行此操作,但您不应该在构造函数中调用virtual方法。

我想知道是否重构以便有一个 initialize Load)方法(你单独调用构造)会更好。可能是protected virtual方法,并通过public static方法公开。

答案 1 :(得分:2)

您可能需要考虑将有关处置的指令作为/在接受IDisposable的构造函数的单独参数内传递。这是XmlReader.Create采用的方法,它接受XmlReaderSettings参数,该参数的CloseInput属性确定在最终处理创建的XmlReader时是否处置基础数据源。

答案 2 :(得分: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; } }
}

答案 3 :(得分:-1)

主要的经验法则是创建一次性对象实例的代码应该处理它。如果您将IDisposable对象传递给方法,则应将其用于所需内容,并将其单独使用。

确保始终执行此操作的一种好方法是使用using ([IDisposable object]) { ... }模式,该模式将在范围完成时自动调用对象上的dispose。

相关问题