I want to create a method that allows me to change arbitrary properties of classes that derive from my base class, the result should look like this: SetPropertyValue("size.height", 50);
– where size
is a property of my derived class and height
is a property of size
.
I'm almost done with my implementation but there's one final obstacle that I want to solve before moving on, to describe this I will first have to explain my implementation a bit:
What the SetPropertyValue
method does is this:
size
)Some example code to clarify further:
private static Dictionary<RuntimeTypeHandle, object> EditableTypes; //property-modifier-dictionary
protected void SetPropertyValue<T>(EditablePropertyMap<T> map, string property, object value) {
var property = map[property]; // get the property modifier
property.Set((T)this, value); // use the set delegate (encapsulated in a method)
}
In the above code, T
is the Type of the actual (derived) class. I need this type for the get/set delegates. The problem is how to get the EditablePropertyMap<T>
when I don't know what T is.
My current (ugly) solution is to pass the map in an overriden virtual method in the derived class:
public override void SetPropertyValue(string property, object value) {
base.SetPropertyValue((EditablePropertyMap<ExampleType>)EditableTypes[typeof(ExampleType)], property, value);
}
What this does is: get the correct dictionary containing the property modifiers of this class using the class's type, cast it to the appropiate type and pass it to the SetPropertyValue
method.
I want to get rid of the SetPropertyValue
method in my derived class (since there are a lot of derived classes), but don't know yet how to accomplish that. I cannot just make a virtual GetEditablePropertyMap<T>
method because I cannot infer a concrete type for T then. I also cannot acces my dictionary directly with a type and retrieve an EditablePropertyMap<T>
from it because I cannot cast to it from object
in the base class, since again I do not know T
.
I found some neat tricks to infere types (e.g. by adding a dummy T
parameter), but cannot apply them to my specific problem. I'd highly appreciate any suggestions you may have for me.
EDIT: Although this is already a wall of text, I have to add some more notes:
EditablePropertyMap
s as objects because I found no other way (different generic type parameters) – perhaps this can be refined as well.To clarify what EditableProperty
/ EditablePropertyMap
does:
public class EditableProperty<T> {
private Func<T, object> getter;
private Action<T, object> setter;
...
public object Get(T obj) { return getter(obj); }
public void Set(T obj, object value) { setter(obj, value);}
The map is a mere wrapper for a Dictionary<string, EditableProperty<T>>
.
http://msdn.microsoft.com/en-us/library/system.object.gettype.aspx - have a map of EditablePropertyMaps keyed by Type, in SetPropertyValue get the map for the derived type by GetType(). Am I missing something?
Additionally:
public abstract class EditablePropertyMap { }
public class EditablePropertyMap<T> : EditablePropertyMap { }
Edit: I guess I was poorly suggesting to just ditch the generics and pass just objects, but now I see you want to keep the delegates generic. How about the following - I rearranged some stuff but it doesn't change the gist:
abstract class EditableProperty { }
class EditableProperty<T> : EditableProperty {
public void Set(T obj, object value) { Console.WriteLine(value); }
}
abstract class EditablePropertyMap {
private static Dictionary<Type, EditablePropertyMap> maps = new Dictionary<Type, EditablePropertyMap>();
abstract public void AddProperty(string name, EditableProperty property);
abstract public void SetPropertyValue(object obj, string name, object value);
static public EditablePropertyMap Get(Type t) {
EditablePropertyMap map;
if(!maps.TryGetValue(t, out map)) {
map = (EditablePropertyMap)Activator.CreateInstance(
typeof(EditablePropertyMap<>)
.MakeGenericType(t));
maps.Add(t, map);
}
return map;
}
}
class EditablePropertyMap<T> : EditablePropertyMap {
private Dictionary<string, EditableProperty<T>> properties = new Dictionary<string, EditableProperty<T>>();
public override void AddProperty(string name, EditableProperty property) {
properties.Add(name, (EditableProperty<T>)property);
}
public override void SetPropertyValue(object obj, string name, object value) {
EditableProperty<T> property = properties[name];
property.Set((T)obj, value);
}
}
class DynamicObjectBase {
public void SetPropertyValue(string name, object value) {
EditablePropertyMap map = EditablePropertyMap.Get(GetType());
map.SetPropertyValue(this, name, value);
}
}
class SomeDynamicObject : DynamicObjectBase {
}
class Program {
static void Main(string[] args) {
EditablePropertyMap map = EditablePropertyMap.Get(typeof(SomeDynamicObject));
map.AddProperty("wut", new EditableProperty<SomeDynamicObject>());
SomeDynamicObject o = new SomeDynamicObject();
o.SetPropertyValue("wut", 1);
}
}
I also didn't do the part of making the EditableProperties from just a type at init, but I assume you've already done all that anyway.
I'm not sure if you'd like a design change this big, but I think the curiously recurring template pattern may be what you need:
class Super<TDerived> where TDerived : Super<TDerived>
{
//Now we have access to TDerived here
}
class Derived : Super<Derived> { ... }
You could also try saying something like:
typeof(MyClass).GetMethod("SetPropertyValue")
.MakeGenericMethod(new Type[] { derivedType })
.Invoke(dictionary, object[] { map, property, value });
but this would be insanely slow.
You can't do this quickly without knowing the type at compile-time, because by definition, you have to bind the methods at compile-time in order to avoid a runtime hit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With