问题
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.
回答1:
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:
typeof(T).GetProperties()
with:
// You need null-checks around this:
// you can't realistically continue if target is null anyway.
target.GetType().GetProperties()
This of course introduces a minor semantic change in your code, in that it prevents scenarios when one would would want to pass a Giraffe
but only want Animal
properties copied over. It will also blow up if source
and target
are 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 MakeGenericMethod
to allow the type-argument to be provided "dynamically". This maintains the original semantics of the code (untested):
typeof(MyClass).GetMethod("Load")
.MakeGenericMethod(property.PropertyType)
.Invoke(null, property.GetValue(target, null),
property.GetValue(source, null), deep);
Btw, there's no reason you can't use dynamic
to 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.
来源:https://stackoverflow.com/questions/4937650/cast-object-without-any-more-info-than-its-system-type