2

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?

Brett Caswell
  • 1,486
  • 1
  • 13
  • 25
roroinpho21
  • 732
  • 1
  • 11
  • 27
  • I’m not sure I entirely understand what you are trying to do. Just get all objects of type T from the dictionary, while excluding anything that isn’t type T? What if there are multiple T objects, should you return them all or just the first? – Neil.Work Jan 05 '19 at 19:57
  • Your `T` needs a default constructor, why not constrain it with `where T: new` and just `var obj = new T();`. What are you trying to do? Is it just to build a dictionary that represents the properties (name and value) of an object. For that, you don't need any casting (you are storing it in a `Dictionary`, so leaving them as an object is all that is needed – Flydog57 Jan 05 '19 at 20:04
  • is this just an exercise? I suggest you look at `System.Dynamic.ExpandoObject` aka `dynamic`. also, `.ofType` and `.Cast` in Linq. – Brett Caswell Jan 05 '19 at 20:05
  • I have a dictionary, each key is a property name and each value is the value for that property. I want to convert the dictionary into a concrete type. 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 a class derived from object and dictionary value is another dictionary – roroinpho21 Jan 05 '19 at 20:25
  • There is another duplicate [How do I use reflection to call a generic method?](https://stackoverflow.com/questions/232535/how-do-i-use-reflection-to-call-a-generic-method) – roroinpho21 Jan 05 '19 at 21:32
  • 1
    @roroinpho21 using reflecting to call your generic method recursively is not "the solution". it is an answer, and probably not the best one. there a several ways you can implement/answer a question like this, such an answer/implementation should be in the form of an answer and voted on (not the in the question). – Brett Caswell Jan 06 '19 at 20:41

2 Answers2

2

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;
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • I think your solution is only for primitive types. otherwise I should call recursive the method but I don't know how to write the cast – roroinpho21 Jan 05 '19 at 20:27
  • 1
    Type.IsAssignableFrom handles class hierarchies quite well, it is definitely not only for primitive types. – Kasper van den Berg Jan 05 '19 at 21:09
  • this certainly works, but I would change all `Dictionary` references to `IDictionary` so that I can work with `System.Dynamic.ExpandoObject` as a parameter. – Brett Caswell Jan 06 '19 at 23:19
  • also it's probably worth mentioning that `Convert.ChangeType` requires `IConvertible` be implemented on the class. and `T` in that generic should have `where T : class, new()` – Brett Caswell Jan 06 '19 at 23:21
  • for reference, `System.Dynamic.ExpandoObject` https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=netframework-4.7.2 – Brett Caswell Jan 06 '19 at 23:25
  • 1
    @BrettCaswell: The primitive types all implement `IConvertible`. The constraints are not strictly necessary, as we are working with Reflection, but are certainly a good idea. Working with dynamics requires you to write the property names in code, which is not the case here. – Olivier Jacot-Descombes Jan 07 '19 at 13:33
-1

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

Zain Ali
  • 65
  • 13