C#:从XML实例化类

时间:2020-03-05 18:44:07  来源:igfitidea点击:

我所拥有的是一个类的集合,这些类全部实现相同的接口,但在幕后却可能有很大的不同。我希望有一个配置文件控制启动程序时将哪些类放入集合中,采取类似以下内容的方法:

<class1 prop1="foo" prop2="bar"/>

并将其变成:

blah = new class1();
blah.prop1="foo";
blah.prop2="bar";

以非常通用的方式。我不知道该怎么做的是在配置文件中使用字符串" prop1",并将其转换为代码中的实际属性访问器。 Cto中是否有任何元编程工具允许这样做?

解决方案

回答

反思就是你想要的。反射+ TypeConverter。没有更多的时间来解释,而只是用谷歌搜索,我们应该会顺利进行的。或者,我们可以只使用xml序列化器,但是必须遵循一种格式,但是效果很好。

回答

将类与xml序列化或者从xml序列化可能更容易,然后可以简单地将XmlReader(正在读取配置文件)传递给反序列化器,其余的工作将由我们完成。

这是一篇关于序列化的不错的文章

编辑

我想补充一件事,即使反射功能强大,它也需要我们了解有关类型的一些知识,例如参数等。

序列化为XML不需要任何这些,并且通过确保将完全限定的类型名称写入XML文件,我们仍然可以具有类型安全性,因此可以自动加载相同的类型。

回答

大量的元编程设施。

具体来说,我们可以获取包含这些类的程序集的引用,然后轻松地从其名称获取类的"类型"。请参见Assembly.GetType方法(字符串)。

从那里,我们可以使用Activator或者Type本身的构造函数实例化该类。请参见Activator.CreateInstance方法。

一旦有了实例,就可以再次使用Type对象来设置属性。请参见Type.GetProperty方法和/或者Type.GetField方法以及PropertyInfo.SetValue方法。

回答

我们正在寻找反射或者XML序列化。

使用反射,我们可以使用类似这样的内容来查找类型

public IYourInterface GetClass(string className)
{
    foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) 
    {            
        foreach (Type type in asm.GetTypes())
        {
            if (type.Name == className)
                return Activator.CreateInstance(type) as IYourInterface;
        }   
    }

    return null;
}

请注意,这将遍历所有程序集。我们可能希望将其减少为仅包含当前正在执行的程序集。

为了分配属性值,我们还可以使用反射。遵循以下原则

IYourInterface o = GetClass("class1");
o.GetType().GetProperty("prop1").SetValue(o, "foo", null);

尽管反射可能是最灵活的解决方案,但我们也应该查看XML序列化,以免自己费力。

回答

反射使我们可以做到这一点。我们可能还需要查看XML序列化。

Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);

回答

我还建议其他人已经提到过Xml序列化。这是我放在一起演示的示例。属性用于将Xml中的名称连接到数据结构中的实际属性名称和类型。属性还列出了所有可以放入Things集合的允许类型。此集合中的所有内容都必须具有一个公共基类。我们说我们已经有一个公共接口-但我们可能必须将其更改为抽象基类,因为当" Thing"是接口时,此代码示例无法立即工作。

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            string xml =
                "<?xml version=\"1.0\"?>" + 
                "<config>" +
                "<stuff>" + 
                "  <class1 prop1=\"foo\" prop2=\"bar\"></class1>" +
                "  <class2 prop1=\"FOO\" prop2=\"BAR\" prop3=\"42\"></class2>" +
                "</stuff>" +
                "</config>";
            StringReader sr = new StringReader(xml);
            XmlSerializer xs = new XmlSerializer(typeof(ThingCollection));
            ThingCollection tc = (ThingCollection)xs.Deserialize(sr);

            foreach (Thing t in tc.Things)
            {
                Console.WriteLine(t.ToString());
            }
        }
    }

    public abstract class Thing
    {
    }

    [XmlType(TypeName="class1")]
    public class SomeThing : Thing
    {
        private string pn1;
        private string pn2;

        public SomeThing()
        {
        }

        [XmlAttribute("prop1")]
        public string PropertyNumber1
        {
            get { return pn1; }
            set { pn1 = value; }
        }

        [XmlAttribute("prop2")]
        public string AnotherProperty
        {
            get { return pn2; }
            set { pn2 = value; }
        }
    }

    [XmlType(TypeName="class2")]
    public class SomeThingElse : SomeThing
    {
        private int answer;

        public SomeThingElse()
        {
        }

        [XmlAttribute("prop3")]
        public int TheAnswer
        {
            get { return answer; }
            set { answer =value; }
        }
    }

    [XmlType(TypeName = "config")]
    public class ThingCollection
    {
        private List<Thing> things;

        public ThingCollection()
        {
            Things = new List<Thing>();
        }

        [XmlArray("stuff")]
        [XmlArrayItem(typeof(SomeThing))]
        [XmlArrayItem(typeof(SomeThingElse))]
        public List<Thing> Things
        {
            get { return things; }
            set { things = value; }
        }
    }
}

回答

我最近做了一些非常相似的事情,我使用了一个抽象工厂。实际上,我们可以在此处看到基本概念:

抽象工厂设计模式