我正在编写一个程序,其界面类似于Windows资源管理器。在此资源管理器视图中,可以存在各种类型的对象,但每个对象都将实现相同的接口,因此资源管理器和将对其执行操作的程序的其他部分可以以相同的方式处理它们。所以,让我们说我有接口:
public interface IMyObject
{
IMyObject Parent { get; }
string DisplayName { get; }
bool IsParent { get; }
}
出于显而易见的原因,我不想在我的应用中设置这些属性的任何内容。它们都应该通过对象的构造函数来设置。所以对象看起来像:
public class MyFileObject : IMyObject
{
public IMyObject Parent { get; protected set; }
public string DisplayName { get; protected set; }
bool IsParent { get; protected set; }
public MyFileObject(IMyObject parent, string displayName)
{
Parent = parent;
DisplayName = displayName;
IsParent = false;
}
}
在处理的某个时刻,这些对象被序列化为XML文件并保存到磁盘。对象的类型保存为XML属性。为了防止复制一堆代码,我首先想到创建一个可以从提供的XML创建这些对象的对象工厂。但是,由于在解析XML之前我不知道我有什么类型的对象,所以我不能做新的MyFileObject(...)"在工厂里。我可以使用反射从XML创建对象类型,但速度很慢。然后我遇到了设置受保护属性的问题。再一次,反思又慢了。
所以我的问题是,如何最好地使用提供的XML创建这些对象,并希望不必让我的所有属性获取/设置?我正在使用Castle Windsor,并简要介绍了它的工厂实现,但在这种情况下无法找到使其工作的方法。
编辑:我昨天晚些时候意识到我没有全面了解你的情况。问题出现的地方有时我不得不从Xml创建对象,有时候我必须创建它们并且#34;在飞行中"因为他们在机器上找到并添加到父母。我试图建立一个工厂,提供从任一来源创建对象的标准方法。 Richard Naud的回答看起来仍然是我所做的最好的回答。我不太了解IL部分,但Linq Expresssions看起来很有前途。
答案 0 :(得分:4)
这里有两种可能表现出色的可能性。
使用IL emit构建对象:http://ayende.com/blog/3167/creating-objects-perf-implications。
这需要在工厂内为每个具体类型创建和编译构造函数委托,然后使用XML的type
属性(参见下面的示例)来选择要调用的属性。
使用已编译的lambda表达式:http://rogeralsing.com/2008/02/28/linq-expressions-creating-objects/。在这里,还有一个按具体类型编译的表达式,并根据类型名称调用适当的表达式。
在旁注中,您必须在序列化时考虑Parent属性。就像现在一样,Parent属性将为每个子节点保存一次XML,也许不是最好的方法。
或者,您可以使用子集合并将其序列化为嵌套对象。这将允许您在集合中添加子项而不必将任何IMyObject实现作为构造函数参数传递,并且如果父属性是必需的,则在添加或删除子项时,只需将Parent设置为所需的值。
可选地,创建实现ICollection东西的抽象类意味着您不必在每种具体类型中重复练习。
public interface IMyObject : ICollection<IMyObject>
{
string DisplayName { get; }
IMyObject Parent { get; }
}
public class MyFileObject : MyAbstractFileObject
{
public MyFileObject(string displayName)
: base(displayName)
{
}
}
public abstract class MyAbstractFileObject : IMyObject
{
private ICollection<IMyObject> children;
protected MyAbstractFileObject(string displayName)
{
this.DisplayName = displayName;
this.children = new Collection<IMyObject>();
}
public IMyObject Parent { get; protected set; }
public string DisplayName { get; protected set; }
public void Add(IMyObject child)
{
this.children.Add(child);
child.Parent = this;
}
public void Remove(IMyChild child)
{
this.children.Remove(child);
child.Parent = null;
}
// other ICollection<IMyObject> members
}
将对象树序列化为:
<Object type="Namespace.MyFileObject" displayName="...">
<Object type="Namespace.MyFileObject" displayName="...">
<Object type="Namespace.MyFileObject" displayName="..." />
<Object type="Namespace.MyOtherFileObject" displayName="..." />
</Object>
<Object type="Namespace.MyOtherFileObject" displayName="..." />
</Object>
对于对象构造没有非常简洁的回应,我希望它可以帮助你。
答案 1 :(得分:0)
我建议您介绍IMyObjectReader
接口,并将每个IMyObject
实现与相应的读者配对。
一些代码来演示这种方法:
interface IMyObject
{
// common properties omitted
}
interface IMyObjectReader
{
IMyObject Parse(XElement xml, IMyObject parent);
}
public class MyFileObject:IMyObject
{
// common properties omitted
public string Property1 { get; set; }
}
public class MyPictureObject:IMyObject
{
// common properties omitted
public string Property2 { get; set; }
}
class MyFileObjectReader : IMyObjectReader
{
public IMyObject Parse(XElement xml, IMyObject parent)
{
var displayName = xml.Attribute("DisplayName").Value;
return new MyFileObject(parent,displayName)
{
Property1 = xml.Attribute("Property1").Value
};
}
}
class MyPictureObjectReader : IMyObjectReader
{
public IMyObject Parse(XElement xml, IMyObject parent)
{
return new MyPictureObject(parent)
{
Property2 = xml.Attribute("Property2").Value
};
}
}
class MyObjectGraphXmlReader
{
Dictionary<string, IMyObjectReader> readerMap;
public MyObjectGraphXmlReader()
{
readerMap = new Dictionary<string, IMyObjectReader>
{
{"File", new MyFileObjectReader()},
{"Picture", new MyFileObjectReader()},
//etc
};
}
public IEnumerable<IMyObject> ReadMyObjects(XElement xmlRoot)
{
return xmlRoot.Elements()
.Select((e)=>ParseElement(e,null));
}
private IMyObject ParseElement(XElement element, IMyObject parent)
{
string readerKey = element.Attribute("Type").Value;
var reader = readerMap[readerKey];
var myObject = reader.Parse(element, parent);
foreach (var subElement in element.Elements())
{
ParseElement(subElement, parent);
}
return myObject;
}
}