То, что у меня есть, это коллекция классов, которые реализуют один и тот же интерфейс, но могут довольно сильно отличаться друг от друга. Я хочу иметь файл конфигурации, управляющий тем, какие классы попадают в коллекцию после запуска программы, принимая что-то похожее на:
<class1 prop1="foo" prop2="bar"/>
и превращая это в:
blah = new class1();
blah.prop1="foo";
blah.prop2="bar";
Очень общим способом. То, что я не знаю, как сделать, это взять строку prop1
в файле конфигурации и превратить ее в фактический метод доступа к свойствам в коде. Есть ли в C # средства метапрограммирования, позволяющие это сделать?
Отражение позволяет Вам делать это. Также можно хотеть посмотреть Сериализация XML .
Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);
Может быть легче сериализировать классы к/от xml, можно тогда просто передать XmlReader (который читает файл конфигурации) к deserializer, и это сделает остальных для Вас..
Это - довольно хорошая статья о сериализации
Одна вещь, которую я хотел бы добавить, даже при том, что отражение мощно, это требует, чтобы Вы знали некоторый материал о типе, таком как параметры и т.д.
, для Сериализации к 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 =
"<?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; }
}
}
}
Отражательная или сериализация 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 для пропуска выполнения тяжелого подъема себя.
Много средств метапрограммирования.
А именно, можно получить ссылку на блок, который содержит эти классы, тогда легко доберитесь Type
из класса с его имени. См. блок. Метод GetType (Строка) .
Оттуда, можно инстанцировать класса с помощью Activator
или конструктор Type
самого. См. Активатор. Метод CreateInstance .
, Как только у Вас есть экземпляр, можно установить свойства путем нового использования эти Type
объект. См. Тип. Метод GetProperty и/или Тип. Метод GetField вперед PropertyInfo. Метод SetValue .
eclipse -clean
впоследствии).
– Philipp Wendler
03.10.2019, 08:09
Отражение - то, что Вы хотите. Отражение + TypeConverter. Не имейте намного большего количества времени, чтобы объяснить, но просто погуглить тех, и необходимо хорошо быть на пути. Или Вы могли просто использовать xml сериализатор, но тогда необходимо придерживаться формата, но работает отлично.