What I have is a collection of classes that all implement the same interface but can be pretty wildly different under the hood. I want to have a config file control which of the
I think you can utilize Dynamics here. Create ExpandoObject, it can be used either as Dictionary for setting properties from xml config.
Plenty of metaprogramming facilities.
Specifically, you can get a reference to the assembly that holds these classes, then easily get the Type
of a class from its name. See Assembly.GetType Method (String).
From there, you can instantiate the class using Activator
or the constructor of the Type
itself. See Activator.CreateInstance Method.
Once you have an instance, you can set properties by again using the Type
object. See Type.GetProperty Method and/or Type.GetField Method along PropertyInfo.SetValue Method.
Reflection allows you to do that. You also may want to look at XML Serialization.
Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);
Reflection or XML-serialization is what you're looking for.
Using reflection you could look up the type using something like this
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;
}
Note that this will go through all assemblies. You might want to reduce it to only include the currently executing assembly.
For assigning property values you also use reflection. Something along the lines of
IYourInterface o = GetClass("class1");
o.GetType().GetProperty("prop1").SetValue(o, "foo", null);
While reflection might be the most flexible solution you should also take a look at XML-serialization in order to skip doing the heavy lifting yourself.
I would also suggest Xml serialization as others have already mentioned. Here is a sample I threw together to demonstrate. Attributes are used to connect the names from the Xml to the actual property names and types in the data structure. Attributes also list out all the allowed types that can go into the Things
collection. Everything in this collection must have a common base class. You said you have a common interface already -- but you may have to change that to an abstract base class because this code sample did not immediately work when Thing
was an interface.
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; }
}
}
}
It may be easier to serialise the classes to/from xml, you can then simply pass the XmlReader (which is reading your config file) to the deserializer and it will do the rest for you..
This is a pretty good article on serialization
One thing I would like to add, even though reflection is powerful, it requires you to know some stuff about the type, such as parameters etc.
Serializing to XML doesnt need any of that, and you can still have type safety by ensuring you write the fully qualified type name to the XML file, so the same type is automatically loaded.