我有一个小的对象层次结构,通常是从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 ()
构造函数中计算的属性应该是基类的一部分,不应该由子类计算(或可以在子类中访问)Node
的主体移动到主体中的using语句中GenerateRaw ()
构造函数。但重复要求调用每个构造函数并且不能保证它为每个子类型运行(CompositeNode
不是Node
,在语义上,没有初始化这些属性)给了我heebie -jeebies比这里的(潜在)资源泄漏要糟糕得多。答案 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。