C#:从XML实例化类

我所拥有的是一系列类,它们都实现了相同的界面,但在引擎盖下可能会非常不同。 我希望有一个配置文件控制在启动程序时哪个类进入集合,采取看起来像这样的东西:

 

并把它变成:

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

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

reflection允许你这样做。 您还可以查看XML序列化 。

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

将类序列化到/从xml可能更容易,然后您可以简单地将XmlReader(正在读取您的配置文件)传递给反序列化器,它将为您完成剩下的工作。

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

编辑

我想补充一点,即使reflection很强大,它也需要你知道一些关于类型的东西,比如参数等。

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

我还建议其他人已经提到的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 = "" + "" + "" + " " + " " + "" + ""; 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 things; public ThingCollection() { Things = new List(); } [XmlArray("stuff")] [XmlArrayItem(typeof(SomeThing))] [XmlArrayItem(typeof(SomeThingElse))] public List Things { get { return things; } set { things = value; } } } } 

您正在寻找reflection或XML序列化。

使用reflection你可以使用这样的东西查找类型

 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; } 

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

要分配属性值,还可以使用reflection。 有点像

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

虽然reflection可能是最灵活的解决方案,但您还应该看一下XML序列化 ,以便自己跳过繁重的工作。

大量的元编程设施。

具体来说,您可以获得对包含这些类的程序集的引用,然后从名称中轻松获取类的Type 。 请参见Assembly.GetType方法(String) 。

从那里,您可以使用ActivatorType本身的构造函数来实例化该类。 请参阅Activator.CreateInstance方法 。

有了实例后,可以再次使用Type对象设置属性。 请参阅PropertyInfo.SetValue方法中的 Type.GetProperty方法和/或Type.GetField 方法 。

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

抽象工厂设计模式

我想你可以在这里使用Dynamics。 创建ExpandoObject,它可以用作Dictionary来设置xml config中的属性。

反思就是你想要的。 Reflection + TypeConverter。 没有太多时间来解释,但只是谷歌那些,你应该顺利。 或者你可以使用xml序列化程序,但是你必须遵循一种格式,但效果很好。