Can I add attributes to an object property at runtime? - c#

For example I want to remove or change below property attributes or add a new one. Is it possible?
[XmlElement("bill_info")]
[XmlIgnore]
public BillInfo BillInfo
{
get { return billInfo; }
set { billInfo = value; }
}

(edit - I misread the original question)
You cannot add actual attributes (they are burned into the IL); however, with XmlSerializer you don't have to - you can supply additional attributes in the constructor to the XmlSerializer. You do, however, need to be a little careful to cache the XmlSerializer instance if you do this, as otherwise it will create an additional assembly per instance, which is a bit leaky. (it doesn't do this if you use the simple constructor that just takes a Type). Look at XmlAttributeOverrides.
For an example:
using System;
using System.Xml.Serialization;
public class Person
{
static void Main()
{
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = false;
attribs.XmlElements.Add(new XmlElementAttribute("personName"));
overrides.Add(typeof(Person), "Name", attribs);
XmlSerializer ser = new XmlSerializer(typeof(Person), overrides);
Person person = new Person();
person.Name = "Marc";
ser.Serialize(Console.Out, person);
}
private string name;
[XmlElement("name")]
[XmlIgnore]
public string Name { get { return name; } set { name = value; } }
}
Note also; if the xml attributes were just illustrative, then there is a second way to add attributes for things related to data-binding, by using TypeDescriptor.CreateProperty and either ICustomTypeDescriptor or TypeDescriptionProvider. Much more complex than the xml case, I'm afraid - and doesn't work for all code - just code that uses the component-model.

It is not possible to add/remove attributes from a class at runtime.
It is possible however to update the way XML serialization works at runtime without needing to edit attributes. See Marc's post.
EDIT Updated

Related

Serialize an object to XML without using attributes

Is it possible to control XmlSerializer/DataContractSerializer behavior without specifying any serialization attributes?
To be more specific, I want to control the serialization (XmlIgnore, custom attribute name etc.), but without decorating my properties.
Use cases:
A large existing class, which I don't wish to pollute with serialization attributes
Serializing a class for which no source code is available
Switching from using XmlSerializer to DataContractSerializer to JSON without changing class code
For example, how would I serialize the following without uncommenting the attributes:
// [Serializable]
public MyClass
{
// [XmlIgnore] - The following property should not be serialized, without uncommenting this line
public int DontSerializeMeEvenThoughImPublic { get; set; }
// [XmlAttribute("another_name")] - should be serialized as 'another_name', not 'SerializeMeAsXmlAttribute'
public double SerializeMeAsXmlAttribute { get; set; }
// [DataMember] - should be included
private string IWantToBeSerializedButDontDecorateMeWithDataMember { get; set; }
}
You can't (do it elegantly).
The only way to modify the way the XmlSerializer serializes classes is by using attributes (by the way SerializableAttribute is not required). The DataContractSerializer is even worse.
One possible solution is to create intermediate classes for XML serialization/desrialization and use Automapper to copy the data between the "real" class and mediator.
I've used this approach to keep the front end XML serialization concerns outside of my business logic data types.
I know this is an old question, but for the XmlSerializer part, it's interesting that no one has suggested the use of Attribute overrides.
Although not solving the Private property, but AFAIK you can't do that with attributes either, so the only route there would be the IXmlSerializable interface.
But what you can do by adding Attributes should be possible with overrides as well.
The following should work for the change wishes reflected by the outcommented XmlAttributes:
public class Program
{
public static void Main()
{
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(MyClass), "DontSerializeMeEvenThoughImPublic", new XmlAttributes { XmlIgnore = true });
overrides.Add(typeof(MyClass), "SerializeMeAsXmlAttribute", new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("another_name") });
XmlSerializer serializer = new XmlSerializer(typeof(MyClass), overrides);
using (var writer = new StringWriter())
{
serializer.Serialize(writer, new MyClass());
Console.WriteLine(writer.ToString());
}
}
}
Serialization via XmlSerializer should work without the attributes.

In C# how do I deserialize XML from an older object into the updated object and ignore missing xml elements?

What I have is a custom settings file that I serialize/deserialize using an XmlSerializer. I have no schema defined and no serialization tags in my object definition, just straight object serialization (although I will add them if needed).
My issue is that I need to add data members to the object. If I do that I know that the old settings file will not deserialize.
Is there a way to specify default values for the added members or some simple way to ignore them if they are missing from the XML?
From MSDN
Best Practices To ensure proper versioning behavior, follow these rules when modifying a type from version to version:
When adding a new serialized field, apply the OptionalFieldAttribute
attribute.
When removing a NonSerializedAttribute attribute from a field (that
was not serializable in a previous version), apply the
OptionalFieldAttribute attribute.
For all optional fields, set meaningful defaults using the
serialization callbacks unless 0 or null as defaults are acceptable.
I have tried to simulate your case where in new version of class have new member named Element2. initialized my new member to "This is new member" here is full proof
Test1 assumes you serialized with old definition of Root class with Just one Element1
Test2 when you serialized and de serialized with new definition of Root Class
To answer your question any way to provide default values you should use "OptionalField"
using System;
using System.Runtime.Serialization;
using System.IO;
public class Test
{
[Serializable]
public class Root
{
[OptionalField(VersionAdded = 2)] // As recommended by Microsoft
private string mElement2 = "This is new member";
public String Element1 { get; set; }
public String Element2 { get { return mElement2; } set { mElement2 = value; } }
}
public static void Main(string[] s)
{
Console.WriteLine("Testing serialized with old definition of Root ");
Console.WriteLine(" ");
Test_When_Original_Object_Was_Serialized_With_One_Element();
Console.WriteLine(" ");
Console.WriteLine("Testing serialized with new definition of Root ");
Console.WriteLine(" ");
Test_When_Original_Object_Was_Serialized_With_Two_Element();
Console.ReadLine();
}
private static void TestReadingObjects(string xml)
{
System.Xml.Serialization.XmlSerializer xmlSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(Root));
System.IO.Stream stream = new MemoryStream();
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(xml);
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;
Root r = (Root)xmlSerializer.Deserialize(stream);
Console.WriteLine(string.Format("Element 1 = {0}", r.Element1));
Console.WriteLine(string.Format("Element 2 = {0}", r.Element2 == null ? "Null" : r.Element2));
}
private static void Test_When_Original_Object_Was_Serialized_With_One_Element()
{
TestReadingObjects(#"<Root> <Element1>1</Element1> </Root>");
}
private static void Test_When_Original_Object_Was_Serialized_With_Two_Element()
{
TestReadingObjects(#"<Root> <Element1>1</Element1> <Element2>2</Element2> </Root>");
}
}
// here is the output
It should deserialize just fine, it will just use the default constructor to initialize the items. So they will remain unchanged.
You need to manually handle it using custom methods and marking them with the appropriate attributes OnSerializing/OnSerialized/OnDeserializing/OnDeserialized and manully determine how to initialize the values (if it can be done)
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializingattribute.aspx
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializedattribute.aspx
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializingattribute.aspx
a better way is to determine the version before hand and use a strategy pattern to do the deserialization. This isn't always possible so use what I suggest in that case.
Update: The previous answer is only applicable to binary serialization. For regular Xml you can use this method.
class Program
{
static void Main(string[] args)
{
Deserialize(#"..\..\v1.xml");
}
private static Model Deserialize(string file)
{
XDocument xdoc = XDocument.Load(file);
var verAtt = xdoc.Root.Attribute(XName.Get("Version"));
Model m = Deserialize<Model>(xdoc);
IModelLoader loader = null;
if (verAtt == null)
{
loader = GetLoader("1.0");
}
else
{
loader = GetLoader(verAtt.Value);
}
if (loader != null)
{
loader.Populate(ref m);
}
return m;
}
private static IModelLoader GetLoader(string version)
{
IModelLoader loader = null;
switch (version)
{
case "1.0":
{
loader = new ModelLoaderV1();
break;
}
case "2.0":
{
loader = new ModelLoaderV2();
break;
}
case "3.0": { break; } //Current
default: { throw new InvalidOperationException("Unhandled version"); }
}
return loader;
}
private static Model Deserialize<T>(XDocument doc) where T : Model
{
Model m = null;
using (XmlReader xr = doc.CreateReader())
{
XmlSerializer xs = new XmlSerializer(typeof(T));
m = (Model)xs.Deserialize(xr);
xr.Close();
}
return m;
}
}
public interface IModelLoader
{
void Populate(ref Model model);
}
public class ModelLoaderV1 : IModelLoader
{
public void Populate(ref Model model)
{
model.City = string.Empty;
model.Phone = "(000)-000-0000";
}
}
public class ModelLoaderV2 : IModelLoader
{
public void Populate(ref Model model)
{
model.Phone = "(000)-000-0000";
}
}
public class Model
{
[XmlAttribute(AttributeName = "Version")]
public string Version { get { return "3.0"; } set { } }
public string Name { get; set; } //V1, V2, V3
public string City { get; set; } //V2, V3
public string Phone { get; set; } //V3 only
}
Depending on your requirements, this could be simplified into a single method on the model (or a model loader).
This could also be used for model validation after deserialization.
Edit: you can serialize using the following code
private static void Serialize(Model model)
{
XmlSerializer xs = new XmlSerializer(typeof(Model));
FileStream f = File.Create(#"..\..\v1.xml");
xs.Serialize(f, model);
f.Close();
}
If you follow this pattern it's fairly straightforward:
Handle the serialization/deserialization yourself by implementing ISerializable
Use this to serialize both the members of your object and a serialization version number.
On the deserialization code, run a switch-case statement against the version number. When you start out, you'll just have one version - the initial deserialization code. As you go forward, you'll stamp a newer version number into newly serialized snapshots.
For future versions of your object, always leave the existing deserialization code intact, or modify it to map to members you rename / refactor, and primarily just add a new case statement for the new serialization version.
In this way, you will be able to successfully deserialize previous data even if the serialization snapshot was generated from a previous version of your assembly.
Use the [System.ComponentModel.DefaultValueAttribute] to define DefaultValues for Serialization.
Example from the MSDN:
private bool myVal=false;
[DefaultValue(false)]
public bool MyProperty {
get {
return myVal;
}
set {
myVal=value;
}
}
So if you DeSerialize and the Property is not filled it would use the defaultValue as Value and you can use your old XML to generate the new Object.
If there were Properties removed in the new Version this should go without any problems trough XMLSerialization. (as far as i know)
You can user ExtendedXmlSerializer. This serializer support deserialization old version of xml. Here is an example of deserialize old version of xml
You can even read different versions of an object from one file.
.NET provide quite a lot for serializing/deserializing and versioning.
1) user DataContract / DataMember attributes and DataContractSerializer
2) according to MSDN these changes are breaking
Changing the Name or Namespace value of a data contract.
Changing the order of data members by using the Order property of the DataMemberAttribute.
Renaming a data member.
Changing the data contract of a data member.
3) When a type with an extra field is deserialized into a type with a missing field, the extra information is ignored.
4) When a type with a missing field is deserialized into a type with an extra field, the extra field is left at its default value, usually zero or null.
5) Consider using IExtensibleDataObject for versioning
Data Contract Versioning
Best Practices Data Contract Versioning
IExtensibleDataObject

Find out whether property setter is called in DeSerialization process

Is there a way to find out whether an object property is called as part of the DeSerialization process (e.g. by the XmlSerializationReaderXXX).
Background: A typical scenario is to disable events and complex operations in that case, until the initialization is complete.
One approach I have found, is to "interpret" the stack and look up whether the call is triggered by XmlSerializationReaderXXX, which is not so elegant IMHO. Is there anything better?
public SomeClass SomeProperty
{
get { ..... }
set
{
this._somePropertyValue = value;
this.DoSomeMoreStuff(); // Do not do this during DeSerialization
}
}
-- Update --
As Salvatore has mentioned, somehow similar to How do you find out when you've been loaded via XML Serialization?
I have a possible solution.
public class xxx
{
private int myValue;
[XmlElement("MyProperty")]
public int MyPropertyForSerialization
{
get { return this.myValue; }
set
{
Console.WriteLine("DESERIALIZED");
this.myValue = value;
}
}
[XmlIgnore]
public int MyProperty
{
get { return this.myValue; }
set
{
Console.WriteLine("NORMAL");
this.myValue = value;
}
}
}
class Program
{
static void Main(string[] args)
{
xxx instance = new xxx();
instance.MyProperty = 100; // This should print "NORMAL"
// We serialize
var serializer = new XmlSerializer(typeof(xxx));
var memoryStream = new MemoryStream();
serializer.Serialize(memoryStream, instance);
// Let's print our XML so we understand what's going on.
memoryStream.Position = 0;
var reader = new StreamReader(memoryStream);
Console.WriteLine(reader.ReadToEnd());
// Now we deserialize
memoryStream.Position = 0;
var deserialized = serializer.Deserialize(memoryStream) as xxx; // This should print DESERIALIZED
Console.ReadLine();
}
}
The trick is using the XmlIgnore, it will force the xml serializer to ignore our property, then we use XmlElement to rename the property for serialization with the name of the property we want.
The problem with this technique is that you have to expose a public property for serialization, and is in some way bad because it can virtually be called by everyone.
It will not work if the member is private, unfortunally.
It works, is not totally clean, but is thread safe and don't rely on any flag.
Another possibility is to use something like the Memento pattern.
Using the same trick you can add a property called for example Memento that returns another object that contains properties suitable only for serialization, it can makes things a little cleaner.
Did you think instead of changing approach and using DataContractSerializer? It is much more powerful and produces pure XML. It supports the OnDeserializationCallback mechanism.
Since you got a pretty complex scenario you might want to consider creating a "data core" class which will be actually serialized/deserialized using simple direct way. Then your complex object is constructed from that object and you fire all events/operations as normal. It will make sequence of deserialize -> fire events/operations more explicit and easier to understand.
There's an OnDeserializingAttribute/OnDeserializedAttribute attributes pair. You can set isDeserializing flag while object is being deserialized. I don't know if they play well with XML serialization, though.
For XML Serialization solution could be implementing IXmlSerializable and embedding such logic into the ReadXml()/WriteXml() method
To have finer control of the deserialization process you could implement IXmlSerializable interface for SomeClass - in ReadXML you can then for example have some field set a flag that you are in deserialization... this flag can then be checked in the respective methods... and on completion it needs to be reset.
Another option (though not for XML IIRC) is to implement the above via OnDeserializingAttribute and OnDeserializedAttribute .
I misunderstood the question at first, but you want to ask from within setter if you are called during deserialization. To do that, use a static flag:
[serializable]
class SomeClass
{
public static IsSerializing = false;
SomeProperty
{
set
{
if(IsSerializing) DoYouStuff();
}
}
}
and then set the flag just before the serialization:
try
{
SomeClass.IsSerializing = true;
deserializedClass = (SomeClass)serializer.Deserialize(reader);
}
finaly
{
SomeClass.IsSerializing = false; //make absolutely sure you set it back to false
}
Note that same approach can work even if you deserialize a class that contains a member of your class...
Set a breakpoint on the property, and run in debug mode. It will break at the point of access for the getter/setter that you set the breakpoint on.

Unable to xml serialize custom object

I created a really simple control that contains a list of filter option controls in much the same way that a listbox has a list of listitems.
I'm having some trouble tring to get it to serialize in to viewstate as the serializer appears to be trying to serialize the base class properties and basically i end up with errors like - cannot serialize property "page" What do you think is my problem?
The Code:
[Serializable]
public class FilterOption : Control, ISerializable
{
public event EventHandler Checkchanged;
CheckBox _chk = new CheckBox();
Label _lbl = new Label();
public string Text
{
get { return _lbl.Text; }
set { _lbl.Text = value; }
}
public bool Checked
{
get { return _chk.Checked; }
set { _chk.Checked = value; }
}
public FilterOption()
{
Controls.Add(new LiteralControl("<li>"));
_chk.AutoPostBack = true;
_chk.CssClass = "checkbox";
Controls.Add(_chk);
Controls.Add(_lbl);
_chk.CheckedChanged += new EventHandler(_chk_CheckedChanged);
Controls.Add(new LiteralControl("</li>"));
}
public FilterOption(string Text, bool Checked)
{
Controls.Add(new LiteralControl("<li>"));
_chk.CssClass = "checkbox";
_lbl.Text = Text;
_chk.Checked = Checked;
Controls.Add(_chk);
Controls.Add(_lbl);
_chk.CheckedChanged += new EventHandler(_chk_CheckedChanged);
Controls.Add(new LiteralControl("</li>"));
}
public FilterOption(SerializationInfo info, StreamingContext context)
{
Controls.Add(new LiteralControl("<li>"));
_chk.CssClass = "checkbox";
_lbl.Text = (string)info.GetValue("Text", typeof(string));
_chk.Checked = (bool)info.GetValue("Text", typeof(bool));
Controls.Add(_chk);
Controls.Add(_lbl);
_chk.CheckedChanged += new EventHandler(_chk_CheckedChanged);
Controls.Add(new LiteralControl("</li>"));
}
void _chk_CheckedChanged(object sender, EventArgs e)
{
if (Checkchanged != null)
Checkchanged(this, new EventArgs());
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if(info == null)
throw new System.ArgumentNullException("info");
info.AddValue("Text", _lbl.Text);
info.AddValue("Checked", _chk.Checked);
}
}
I literally only need to serialize the properties added to the serialization info in the GetObjectData method.
I'm using the following code to perform the serialization ...
List<FilterOption> options = new List<FilterOption>();
... add some items to the collection ...
StringWriter writer = new StringWriter();
XmlSerializer ser = new XmlSerializer(typeof(List<FilterOption>));
ser.Serialize(writer, options);
ViewState["Options"] = writer.ToString();
Oh yeh ... i forgot to add ... i got the information from here ...
http://msdn.microsoft.com/en-us/library/ms973893.aspx
(in case it matters)
...
Thx Wardy
First of all you should split your control and serializable data. Second, .net framework contains several serialization types:
Serialization utilities from System.Runtime.Serialization ([BinaryFormatter][1] and [SoapFormatter][2]). Both of this foratters requires [SerializationAttribute][3] for your class or implementing [ISerializable][4] interface (if you need more flexible way controlling serialization process). Those serializers serialize all private fields for current class and all it descendants if that fields does not marked with [NonSerialializedAttribute][5].
Note: this serialization uses during .net remoting.
Xml serialization with [XmlSerializer][6] class. In this case your class should have parameterless constructor and this serializer serialize all public read/write properties for current class and all descendants that does not mark with [XmlIgnoreAttribute][7].
[DataContractSerializer][8]. This serializer requires that you entity should be marked with [DataContractAttribute][9] and all properties should be marked with [DataMemberAttribute][10]. Also this serializer could serialize classes serializable in two previous ways.
In general it's very bad practice try to serialize user control, because it definitely would contains non-serializable fields (that not marked with NonSerializedAttribute). So you'll definitely receive error message during runtime.
The easiest way (and more appropriate from design point of view) is separate serializable data into separate class and choose right serialization technique.
I.e. if you want to use Xml-serialization you should create parameterless constructor for your class and use read/write properties:
public class FilterOption
{
public FilterOption() {}
public string MyLabel{get;set;}
public bool IsChecked{get;set;}
}
and now you could use your previous code:
var options = new List<FilterOption>
{
new FilterOption {MyLabel = "label", IsChecked = false},
new FilterOption {MyLabel = "label2", IsChecked = true}
};
StringWriter writer = new StringWriter();
XmlSerializer ser = new XmlSerializer(typeof(List<FilterOption>));
ser.Serialize(writer, options);
Apparently you cannot serialize a type that inherits a non serializable type even ifyou do not wish to serialize the non-serializable properties of your derived type.
I think this should be classed as a bug since the who point of interfaces like ISerializable is to specify exactly what it is you inted to serialize by manually implementing the method that handles the serialization.
In any case the solution to my particular scenario was to not bother serializing at all and simply save the information of interest in to viewstate which would then be reused on postbacks to rebuild controls in the exact same state ready for the page based postback events to occur.
Shame this isnt better documented somewhere because although microsoft does document the SaveViewState and LoadViewState methods of the page lifecycle they are very vague about how these events might be used, i'm guessing they are hoping someone in the community might provide an example.
I would post my code but its a nasty hack to get everything working so i don't thing it should be mainstream msdn code.
Ok for a small internal app though :)
You should take a look at this link, for XML Serialization
http://msdn.microsoft.com/en-us/library/ms950721.aspx
considering your comments. I have searched again and now I think that you have forgot to add this code in your GetObjectData() function.
base.GetObjectData(si,context);
You cannot serialize your object because it contains objects which aren't serializable. ASP.NET controls (like CheckBox and Label are) are not serializable.
You should create a list of objects instead which contain just the data you really need, which will certainly a boolean value and a string.
You must then recreate the controls from this state on each Post request, but there is no other way I know of.
[Serializable]
public class FilterOption
{
public string MyLabel{get;set;}
public bool IsChecked{get;set}
}
EDIT:
You can put the attribute [NonSerialized] above members you do not want to get serialized.

C#: instantiating classes from XML

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 classes go into the collection upon starting the program, taking something that looks like :
<class1 prop1="foo" prop2="bar"/>
and turning that into :
blah = new class1();
blah.prop1="foo";
blah.prop2="bar";
In a very generic way. The thing I don't know how to do is take the string prop1 in the config file and turn that into the actual property accessor in the code. Are there any meta-programming facilities in C# to allow that?
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);
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
Edit
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.
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; }
}
}
}
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.
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.
I recently did something very similar, I used an abstract factory. In fact, you can see the basic concept here:
Abstract Factory Design Pattern
I think you can utilize Dynamics here. Create ExpandoObject, it can be used either as Dictionary for setting properties from xml config.
Reflection is what you want. Reflection + TypeConverter. Don't have much more time to explain, but just google those, and you should be well on your way. Or you could just use the xml serializer, but then you have to adhere to a format, but works great.

Categories

Resources