Reputation: 742
I have a method in which I like to perform a few different casts using reflection mechanism.
private static T GetObject<T>(Dictionary<string, object> dict)
{
Type type = typeof(T);
var obj = Activator.CreateInstance(type);
foreach (var kv in dict)
{
var p = type.GetProperty(kv.Key);
Type t = p.PropertyType;
Type t2 = kv.Value.GetType();
if (t == t2)
{
p.SetValue(obj, kv.Value);
}
else if (!IsPrimitive(p.PropertyType))
{
p.SetValue(obj, GetObject<class of p>((Dictionary<string, object>) kv.Value)); //???
}
else
{
p.SetValue(obj, (primitive)(kv.Value)); //???
}
}
return (T)obj;
}
EDITED
I have a dictionary and I want to convert it to a custom class, each key from dictionary is a property and each dictionary value is the value for that property. The problems appears in two cases, when both, the class property type and dictionary value type are primitives but they have different type (e.g. property is int but dictionary value is long), the second problem appears when property value is another custom class and in that case the dictionary value is always another dictionary
How can I detect dynamically the necessary cast/casts?
Upvotes: 2
Views: 200
Reputation: 112762
Start by looking if the types are assignment compatible with
if (t.IsAssignableFrom(t2))
Otherwise convert with
object converted = Convert.ChangeType(kv.Value, t);
This should handle a lot of cases.
Since you must call the method recursively with a type only know at runtime, it is better to have a non-generic overloaded version of GetObject
. The original method only calls the non-generic one
private static T GetObject<T>(Dictionary<string, object> dict)
where T : class, new() // Suggested by Brett Caswell.
{
return (T)GetObject(dict, typeof(T));
}
Note that generic type parameters are always resolved at compile-time. Since you must resolve the type dynamically at run-time, they are more of a hindrance here. You could construct a generic method call using reflection, but I don't see any advantage in doing so. It is complicated and not type safe. Type safety can only be enforced by the compiler.
private static object GetObject(Dictionary<string, object> dict, Type objectType)
{
object obj = Activator.CreateInstance(objectType);
foreach (var kv in dict) {
PropertyInfo prop = objectType.GetProperty(kv.Key);
Type propType = prop.PropertyType;
object value = kv.Value;
if (value == null) {
if (propType.IsValueType) { // Get default value of type.
value = Activator.CreateInstance(propType);
}
} else if (value is Dictionary<string, object> nestedDict) {
value = GetObject(nestedDict, propType);
} else if (!propType.IsAssignableFrom(value.GetType())) {
value = Convert.ChangeType(value, propType);
}
prop.SetValue(obj, value);
}
return obj;
}
Upvotes: 2
Reputation: 65
Better approach of this problem is apply one of uncle Bob's S.O.L.I.D principle which is Open close principle. Here is the Classic Example of this principle
public abstract class Shape
{
public abstract double Area();
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area()
{
return Width*Height;
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area()
{
return Radius*Radius*Math.PI;
}
}
by this way you don't have to make if else tree but with implementing the abstract class you have to implement Area method every time you use it with any of your Class. Hope that serve the purpose
Upvotes: -1