我所拥有的是一系列类,它们都实现了相同的界面,但在引擎盖下可能会非常不同。我希望有一个配置文件控制在启动程序时哪些类进入集合,采取类似的东西:
<class1 prop1="foo" prop2="bar"/>
并将其转化为:
blah = new class1();
blah.prop1="foo";
blah.prop2="bar";
以非常通用的方式。我不知道该怎么做的是在配置文件中取字符串prop1
并将其转换为代码中的实际属性访问器。 C#中是否有任何元编程工具允许这样做?
答案 0 :(得分:9)
将类序列化到/从xml可能更容易,然后您可以简单地将XmlReader(正在读取您的配置文件)传递给反序列化器,它将为您完成其余的工作。
This is a pretty good article on serialization
我想补充一点,即使反射很强大,它也需要你知道一些关于类型的东西,比如参数等。
序列化为XML不需要任何这些,并且您仍然可以通过确保将完全限定的类型名称写入XML文件来保证类型安全,因此会自动加载相同的类型。
答案 1 :(得分:9)
Reflection允许您这样做。您还可以查看XML Serialization。
Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);
答案 2 :(得分: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; }
}
}
}
答案 3 :(得分: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-serialization,以便自己跳过繁重的工作。
答案 4 :(得分:3)
大量的元编程设施。
具体来说,您可以获得对包含这些类的程序集的引用,然后从名称中轻松获取类的Type
。请参阅Assembly.GetType Method (String)。
从那里,您可以使用Activator
或Type
本身的构造函数来实例化该类。请参阅Activator.CreateInstance Method。
获得实例后,可以再次使用Type
对象设置属性。请Type.GetProperty Method旁边的Type.GetField Method和/或PropertyInfo.SetValue Method。
答案 5 :(得分:1)
我最近做了一些非常相似的事情,我使用了一个抽象工厂。事实上,你可以在这里看到基本概念:
答案 6 :(得分:1)
我认为你可以在这里使用Dynamics。创建ExpandoObject,它可以用作字典来设置xml config中的属性。
答案 7 :(得分:0)
反思就是你想要的。 Reflection + TypeConverter。没有太多时间来解释,但只是google那些,你应该顺利。或者您可以使用xml序列化程序,但是您必须遵循格式,但效果很好。