C#: 从XML实例化类

13

我手头有一组实现相同接口的类,但在底层实现上可能大相径庭。我希望有一个配置文件能够控制程序启动时哪些类进入这个集合,像这样:

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

并将其转换为:

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

非常简单地说,我的问题是如何将配置文件中的字符串 prop1 转换为代码中实际的属性访问器。在C#中是否有任何元编程工具可以实现这一点?

8个回答

9

反射可以帮助你实现这一目标。您还可以查看XML序列化

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

9

将类序列化/反序列化为xml可能更容易,然后您只需将XmlReader(正在读取配置文件)传递给反序列化程序,它将为您完成其余工作。

这是一篇关于序列化的很好的文章

编辑

我想补充一件事,尽管反射功能强大,但需要您了解有关类型的一些信息,例如参数等。

将对象序列化为XML不需要任何内容,您仍然可以通过确保将完全限定的类型名称写入XML文件来实现类型安全,因此将自动加载相同的类型。


8

正如其他人已经提到的那样,我建议您使用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; }
        }
    }
}

6

您需要的是反射或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序列化,以便跳过自己做繁重工作。


3

1

我最近做了一件非常类似的事情,我使用了抽象工厂。实际上,你可以在这里看到基本概念:

抽象工厂设计模式


1

我认为你可以在这里利用Dynamics。创建ExpandoObject,它可以被用作从xml配置中设置属性的字典。


0

反射是你想要的。反射+类型转换器。我没有太多时间解释,但只需谷歌这些,你应该就能理解了。或者你可以使用 XML 序列化程序,但那样你必须遵守一种格式,不过效果很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接