For a SO question i quite recently wrote a generic extension method that should load an object from another, i.e. assign all the properties of the source to the target and do so recursively if the property is a reference-type. I got quite far using reflection but i hit a problem when it came to the types of properties that are reference-types, here is my first approach:
First approach:
public static void Load<T>(this T target, T source, bool deep)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (property.CanWrite && property.CanRead)
{
if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
{
property.SetValue(target, property.GetValue(source, null), null);
}
else
{
property.GetValue(target, null).Load(property.GetValue(source, null), deep);
}
}
}
}
The problem here is that PropertyInfo.GetValue returns an object, subsequently T will equal object in the recursive call and i can no longer get the properties that the object actually has.
I conceived of a workaround which requires you to pass the Type explicitly, which is quite redundant since in theory it should be possible to manage without:
public static void Load<T>(this T target, Type type, T source, bool deep)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.CanWrite && property.CanRead)
{
if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
{
property.SetValue(target, property.GetValue(source, null), null);
}
else
{
object targetPropertyReference = property.GetValue(target, null);
targetPropertyReference.Load(targetPropertyReference.GetType(), property.GetValue(source, null), deep);
}
}
}
}
I also tried using dynamic targetPropertyReference but then i get a runtime exception that the Load method cannot be found, it is infuriating.
Other than that Convert.ChangeType handily returns a bloody object too and i cannot seem to otherwise cast the object to what it is. Of course i have looked for an answer to this on the net but i have been unsuccessful so far.
I haven’t looked at the full code or considered the full consequences of implementing such a scheme (EDIT: what will happen if there are cyclic references?), but I can give you two short fixes for your specific problem:
Option 1: Dodge the issue
Use the run-time type of the provided “normal” argument, rather than the type-argument.
Replace:
with:
This of course introduces a minor semantic change in your code, in that it prevents scenarios when one would would want to pass a
Giraffebut only wantAnimalproperties copied over. It will also blow up ifsourceandtargetare of different run-time types (having different properties), but you can work around that without too much trouble (e.g. find the deepest common base-type and use its properties instead).Option 2: More reflection / dynamic
The other solution of course, is to use
MakeGenericMethodto allow the type-argument to be provided “dynamically”. This maintains the original semantics of the code (untested):Btw, there’s no reason you can’t use
dynamicto accomplish something very similar. I suspect you’re trying to do the call “as” an extension-method, but that won’t work. Just try the normal “static-method” syntax.