C#:从XML实例化类
我所拥有的是一个类的集合,这些类全部实现相同的接口,但在幕后却可能有很大的不同。我希望有一个配置文件控制启动程序时将哪些类放入集合中,采取类似以下内容的方法:
<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; } } } }
回答
我最近做了一些非常相似的事情,我使用了一个抽象工厂。实际上,我们可以在此处看到基本概念:
抽象工厂设计模式