Factories and C #
I am writing a program that will have an interface similar to Windows Explorer. In this view of the explorer, there can be objects of different types, but each of the objects will implement the same interface so that they can be handled in the same way by the explorer and other parts of the program that will act on them. So, let's say I have an interface:
public interface IMyObject
{
IMyObject Parent { get; }
string DisplayName { get; }
bool IsParent { get; }
}
For obvious reasons, I don't want anything in my application to set these properties. All of them must be set through the object's constructor. Thus, the object will look like this:
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;
}
}
At some point in processing, these objects are serialized to an XML file and saved to disk. The object type is stored as an XML attribute. To avoid duplicating a bunch of code, I first thought about creating a factory object that could create these objects from the provided XML. However, since I donβt know what type of object I have until the XML is parsed, I cannot just do a βnew MyFileObject (...)β in the factory. I can use reflection to create an object type from XML, but this is slow. And then I got the problem of setting protected properties. Again, thinking is, again, slow.
So my question is, how best can I create these objects using the supplied XML and hopefully without having to do all my get / set properties? I'm using Castle Windsor and have a quick look at its factory implementation, but can't figure out a way to get it to work in this case.
Edit: I realized much later yesterday that I didn't give you the full picture. Where the problem arises, I sometimes have to create objects from Xml, and sometimes I have to create them on the fly as they are on the machine and added to the parents. I was trying to come up with a factory that would provide a standard facility for creating objects from any source. Richard Noud's answer still looks like it's going to be the best for what I'm doing. I don't really understand the IL part, but the Linq Expresssions look promising.
source to share
There are two possibilities here that work well.
-
Use IL emit to create your objects: http://ayende.com/blog/3167/creating-objects-perf-implications . This will require creating and compiling a constructor delegate in your factory for each of your specific types, then using an attribute in
type
your XML (see example below) to choose which one to call. -
Use compiled lambda expressions: http://rogeralsing.com/2008/02/28/linq-expressions-creating-objects/ . Here again, one compiled expression on a specific type and referencing the corresponding name based on the type name.
On the side of the note, you should think about the parent property when serializing. As it is now, the Parent property will persist in XML once for each of its children, perhaps not in the best way.
Alternatively, you can use a collection of children and serialize it as nested objects. This will allow you to add children to the collection without passing any of your IMyObject implementations as a constructor argument, and if the Parent property is a requirement, when adding or removing a child from it, just set the parent to the desired value.
Perhaps creating an abstract class that implements the ICollection stuff means you don't have to repeat the exercise on every concrete type.
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
}
Serialize the object tree like this:
<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>
Without being a very concise answer to object construction, I hope this can help you.
source to share
I suggest that you introduce an interface IMyObjectReader
and link each of your implementations IMyObject
to the appropriate reader. Some code demonstrating the approach:
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;
}
}
source to share