【问题标题】:Factories and C#工厂和 C#
【发布时间】:2015-06-07 16:21:04
【问题描述】:

我正在编写一个程序,其界面类似于 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 之前我不知道我拥有什么类型的对象,所以我不能只在工厂中执行“new MyFileObject(...)”。我可以使用反射从 XML 创建对象类型,但这很慢。然后我遇到了设置受保护属性的问题。再一次,反思,再一次,缓慢。

所以我的问题是,我怎样才能最好地使用提供的 XML 创建这些对象,并且希望不必获取/设置我的所有属性?我正在使用 Castle Windsor 并简要查看了它的工厂实现,但无法找到在这种情况下使其工作的方法。

编辑:我昨天晚些时候才意识到我没有给你完整的图片。问题出现的地方是,有时我不得不从 Xml 创建对象,而其他时候我不得不“即时”创建它们,因为它们在机器上被发现并添加到父级。我试图想出一个工厂,它可以提供一种从任一来源创建对象的标准方法。 Richard Naud 的回答看起来仍然是我正在做的最好的。我不太了解 IL 部分,但 Linq 表达式看起来很有希望。

【问题讨论】:

  • 你可以让类型实现一些序列化契约,所以你可以调用一个通用方法,例如相应的 XML 元素,然后需要该类型自行设置其所有属性。
  • 对象不能有getter和setter,接口只有getter吗?
  • 如何将对象序列化为 xml?
  • @Kirill,实际上有一个 Xml 属性,当获得该属性时,它将创建一个具有适当标识属性的 XElement。如果对象是父对象,则将每个子对象的 Xml 属性添加为该对象的元素。设置反向工作。
  • @DrewB 很高兴它可以提供帮助。当我看到你的问题时我不知道,但我觉得它很有趣,所以我不得不寻找答案来满足我的好奇心。 ;)

标签: c# factory


【解决方案1】:

这里有 2 种可能性,可以提供良好的性能。

  1. 使用 IL emit 构造您的对象:http://ayende.com/blog/3167/creating-objects-perf-implications。 这将需要在您的工厂中为每个具体类型创建和编译一个构造函数委托,然后使用 XML 的 type 属性(参见下面的示例)来选择要调用的一个。

    李>
  2. 使用已编译的 lambda 表达式:http://rogeralsing.com/2008/02/28/linq-expressions-creating-objects/。同样在这里,一个按具体类型编译的表达式,并根据类型名称调用适当的表达式。

附带说明,您在序列化时必须考虑 Parent 属性。就像现在一样,Parent 属性将为其每个子属性保存到 XML 一次,这可能不是最好的方法。

或者,您可以使用一组子对象并将其序列化为嵌套对象。这将允许您在集合中添加子项,而无需将任何 IMyObject 实现作为构造函数参数传递,并且如果 Parent 属性是必需的,则在从中添加或删除子项时,只需将 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>

在对象构造上不是很简洁的回答,希望对你有所帮助。

【讨论】:

    【解决方案2】:

    我建议您引入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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-01-05
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多