C #: создание экземпляров классов из XML - PullRequest
13 голосов
/ 28 августа 2008

То, что у меня есть, - это коллекция классов, которые реализуют один и тот же интерфейс, но могут довольно сильно отличаться друг от друга. Я хочу иметь файл конфигурации, управляющий тем, какие классы попадают в коллекцию после запуска программы, принимая что-то похожее на:

<class1 prop1="foo" prop2="bar"/>

и превращая это в:

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

Очень общим способом. То, что я не знаю, как сделать, это взять строку prop1 в файле конфигурации и превратить ее в фактический метод доступа к свойствам в коде. Есть ли в C # средства метапрограммирования, позволяющие это сделать?

Ответы [ 8 ]

9 голосов
/ 28 августа 2008

Отражение позволяет вам сделать это. Вы также можете посмотреть Сериализация XML .

Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);
9 голосов
/ 28 августа 2008

Может быть проще сериализовать классы в / из xml, затем вы можете просто передать XmlReader (который читает ваш файл конфигурации) десериализатору, и он сделает все остальное за вас ..

Это довольно хорошая статья о сериализации

Редактировать

Я хотел бы добавить одну вещь, хотя рефлексия мощная, она требует от вас некоторых сведений о типе, таких как параметры и т. Д.

Сериализация в XML не нуждается в этом, и вы все равно можете иметь безопасность типов, гарантируя, что вы записали полное имя типа в файл XML, чтобы тот же тип автоматически загружался.

8 голосов
/ 28 августа 2008

Я бы также предложил сериализацию 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; }
        }
    }
}
6 голосов
/ 28 августа 2008

Отражение или 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-сериализацию , чтобы пропустить тяжелую работу самостоятельно.

3 голосов
/ 28 августа 2008

Множество возможностей метапрограммирования.

В частности, вы можете получить ссылку на сборку, которая содержит эти классы, а затем легко получить Type класса из его имени. См. Метод Assembly.GetType (String) .

Оттуда вы можете создать экземпляр класса, используя Activator или конструктор самого Type. См. Activator.CreateInstance Method .

Если у вас есть экземпляр, вы можете снова установить свойства, используя объект Type. См. Метод Type.GetProperty и / или Метод Type.GetField вдоль Метод PropertyInfo.SetValue .

1 голос
/ 21 сентября 2012

Я думаю, вы можете использовать Динамику здесь. Создайте ExpandoObject, его можно использовать как словарь для настройки свойств из конфигурации xml.

1 голос
/ 28 августа 2008

Я недавно сделал что-то очень похожее, я использовал абстрактную фабрику. На самом деле, вы можете увидеть основную концепцию здесь:

Абстрактный шаблон дизайна фабрики

0 голосов
/ 28 августа 2008

Отражение - это то, что вы хотите. Reflection + TypeConverter. У меня не так много времени, чтобы объяснить, а просто гуглите, и вы должны быть в пути. Или вы можете просто использовать сериализатор xml, но тогда вам нужно придерживаться формата, но он прекрасно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...