【问题标题】:C#: instantiating classes from XMLC#:从 XML 实例化类
【发布时间】:2010-09-07 01:42:02
【问题描述】:

我所拥有的是一个类的集合,它们都实现了相同的接口,但在底层可能会有很大的不同。我想要一个配置文件控制在启动程序时哪些类进入集合,看起来像:

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

并把它变成:

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

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

【问题讨论】:

    标签: c# xml


    【解决方案1】:

    Reflection 允许您这样做。你也可以看看XML Serialization

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

    【讨论】:

      【解决方案2】:

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

      This is a pretty good article on serialization

      编辑

      我想补充一点,虽然反射很强大,但它需要你了解一些关于类型的东西,比如参数等。

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

      【讨论】:

        【解决方案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 序列化是您正在寻找的。​​p>

          使用反射,您可以使用类似的方式查找类型

          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,以便自己跳过繁重的工作。

          【讨论】:

            【解决方案5】:

            大量的元编程工具。

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

            从那里,您可以使用ActivatorType 本身的构造函数来实例化该类。见Activator.CreateInstance Method

            拥有实例后,您可以再次使用Type 对象来设置属性。请参阅Type.GetProperty Method 和/或Type.GetField Method 以及PropertyInfo.SetValue Method

            【讨论】:

              【解决方案6】:

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

              Abstract Factory Design Pattern

              【讨论】:

                【解决方案7】:

                我认为您可以在这里使用 Dynamics。创建 ExpandoObject,它既可以用作字典,用于从 xml config 设置属性。

                【讨论】:

                  【解决方案8】:

                  反射是你想要的。反射 + 类型转换器。没有太多时间来解释了,只要用谷歌搜索一下,你应该一切顺利。或者您可以只使用 xml 序列化程序,但是您必须遵守格式,但效果很好。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-04-06
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-08-11
                    相关资源
                    最近更新 更多