Factories and C#

395 views Asked by At

I am writing a program that will have an interface similar to Windows Explorer. In this explorer view, there can be objects of various types, but each of the objects will implement the same interface so they can be treated the same way by the explorer and other parts of the program that will be acting on them. So let's say I have the interface:

public interface IMyObject
{
    IMyObject Parent { get; }
    string DisplayName { get; }
    bool IsParent { get; }
}

For obvious reasons I don't want just anything in my app setting these properties. They should all be set through the constructor of the object. So an object would look like:

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 the processing, these objects are serialized to an XML file and saved to disk. The type of the object is saved as an XML attribute. In order to keep from duplicating a bunch of code, I first thought of creating an object factory that could create these objects from the supplied XML. However, since I don't know what type of object I have until the XML is parsed, I can't just do "new MyFileObject(...)" in the factory. I can use reflection to create the type of object from the XML, but that's slow. And then I have the problem of setting the protected properties. Again, reflection, again, slow.

So my question is, how best can I create these objects with the supplied XML and hopefully without having to make all of my properties get/set? I am using Castle Windsor and have looked briefly at its factory implementation, but can't figure out a way to make it work in this case.

Edit: I realized much later yesterday that I hadn't given you the full picture. Where the issue comes in is that sometimes I'm having to create the objects from the Xml and other times I'm having to create them "on the fly" as they're being found on the machine and added to the parent. I was trying to come up with a factory that would provide a standard means of creating the objects from either source. Richard Naud's answer still looks like it will be the best for what I'm doing. I don't really understand the IL portion, but the Linq Expresssions look promising.

2

There are 2 answers

0
RickNo On BEST ANSWER

Here's 2 possibilities which give good performances.

  1. Use IL emit to construct your objects: http://ayende.com/blog/3167/creating-objects-perf-implications. This would require to create and compile a constructor delegate, within your factory, for each of your concrete type, then using the type attribute of your XML (see example below) to choose which one to invoke.

  2. Use compiled lambda expressions: http://rogeralsing.com/2008/02/28/linq-expressions-creating-objects/. Again here, one compiled expression by concrete type and invoking the appropriate one based on the type name.

On a side note, you must think about the Parent property when you'll serialize. As it is now, the Parent property will be saved to XML once for each of its child, maybe not the best approach.

Alternatively you could use a collection of children and serialize it as nested objects. This would allow you to add children in the collection without having to pass any of your IMyObject implementation as constructor argument, and if the Parent property is a requirement, when adding or removing child from it, simply set the Parent to the required value.

Optionally, creating an abstract class implementing the ICollection stuff means you don't have to repeat the exercise in each 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
}

Serializing the object tree as 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 response on the object construction, I hope it can help you out.

0
igorushi On

I propose you to introduce IMyObjectReader interface and to pair each of your IMyObject implementations with corresponding reader. Some code to demonstrate 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;
    }
}