С#: создание экземпляров классов из XML
У меня есть набор классов, которые реализуют один и тот же интерфейс, но могут быть довольно дико различимы под капотом. Я хочу иметь конфигурационный файл управления, который из классов входит в коллекцию при запуске программы, беря что-то вроде:
<class1 prop1="foo" prop2="bar"/>
и превращая это в:
blah = new class1();
blah.prop1="foo";
blah.prop2="bar";
В общем случае. То, что я не знаю, как это сделать, это взять строку prop1
в файле конфигурации и превратить ее в фактический аксессуар свойства в коде. Есть ли в С# объекты метапрограммирования, чтобы это разрешить?
Ответы
Ответ 1
Может быть проще сериализовать классы в/из xml, вы можете просто передать XmlReader (который читает ваш файл конфигурации) в десериализатор, и он сделает все остальное для вас.
Это довольно хорошая статья о сериализации
Изменить
Одна вещь, которую я хотел бы добавить, даже если отражение мощное, это требует, чтобы вы знали некоторые вещи о типе, такие как параметры и т.д.
Сериализация в XML не нуждается в этом, и вы все еще можете иметь безопасность типов, гарантируя, что вы пишете полное имя типа в XML файл, поэтому тот же тип автоматически загружается.
Ответ 2
Отражение позволяет это сделать. Вы также можете посмотреть Сериализация XML.
Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);
Ответ 3
Я бы также предложил сериализацию 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; }
}
}
}
Ответ 4
Отражение или 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-сериализацию, чтобы пропустить выполнение тяжелой работы.
Ответ 5
Множество средств метапрограммирования.
В частности, вы можете получить ссылку на сборку, которая содержит эти классы, а затем легко получить Type
класса от его имени. См. Assembly.GetType Method (String).
Оттуда вы можете создать экземпляр класса с помощью Activator
или конструктора самого Type
. См. Activator.CreateInstance Method.
Как только у вас есть экземпляр, вы можете снова установить свойства, используя объект Type
. См. Метод Type.GetProperty и/или Метод Type.GetField вдоль Свойство PropertyInfo.SetValue.
Ответ 6
Недавно я сделал что-то очень похожее, я использовал абстрактный factory. На самом деле вы можете увидеть базовое понятие здесь:
Аннотация Factory Шаблон проектирования
Ответ 7
Я думаю, вы можете использовать Dynamics здесь. Создайте ExpandoObject, его можно использовать либо как Словарь для задания свойств из xml config.
Ответ 8
Отражение - это то, что вы хотите. Reflection + TypeConverter. У вас не так много времени, чтобы объяснить, а просто Google, и вам должно быть хорошо на вашем пути. Или вы можете просто использовать XML-сериализатор, но тогда вы должны придерживаться формата, но отлично работает.