Question

I have built an expression tree that derives parameters from a class and invokes them to set values in another class. It works very well for most types, but it fails on a derived type. If I have:

public class A
{
    public string MyString {get; set;}
}

and

public class B : A
{

}

If I use this Expression Tree code (where value is the derived instance):

// Get the type of the object for caching and ExpressionTree purposes
var objectType = value.GetType();
// Define input parameter
var inputObject = Expression.Parameter( typeof( object ), "value" );

var properties = objectType.GetProperties( BindingFlags.Instance | BindingFlags.Public );
foreach (var property in properties)
{
    Expression.Property( Expression.ConvertChecked( inputObject, property.DeclaringType ), property.GetGetMethod() )
}

I will receive this exception:

{System.ArgumentException: The method 'My.Namespace.A.get_MyString' is not a property accessor
at System.Linq.Expressions.Expression.GetProperty(MethodInfo mi)...

What am I doing wrong here?

Was it helpful?

Solution

You need to use property itself and not Getter method for it:

foreach (var property in properties)
{
    Expression.Property( 
         Expression.ConvertChecked( inputObject, property.DeclaringType ),
         property);
}

Basically you need to use this Property method instead of this overloaded Property method

UPDATE

For GetGetMethod approach - If var objectType equal to A type, then it works. If it equal to B type, then it doesn't work.

My guess, that the problem is in property.DeclaringType. For both cases it's A type, as property declared in it. But this code will return different ReflectedType for the same property:

var reflectingTypeForA = typeof(A).GetProperty("MyString").GetGetMethod().ReflectedType;
var reflectingTypeForB = typeof(B).GetProperty("MyString").GetGetMethod().ReflectedType;

For A it returns A, and for B type it returns B. My guess is that for B case Expression.Property logic checks that property.DeclaringType is A, but GetGetMethod has ReflectedType equal to B, so it thinks that it's property of another object. I haven't any proofs for that, but only these members are different between two GetGetMethod calls

UPDATE2

Here is a code that used by Expression.Property(MethodInfo method):

private static PropertyInfo GetProperty(MethodInfo mi)
{
    Type type = mi.DeclaringType;
    BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic;
    flags |= (mi.IsStatic) ? BindingFlags.Static : BindingFlags.Instance;
    PropertyInfo[] props = type.GetProperties(flags);
    foreach (PropertyInfo pi in props)
    {
        if (pi.CanRead && CheckMethod(mi, pi.GetGetMethod(true)))
        {
            return pi;
        }
        if (pi.CanWrite && CheckMethod(mi, pi.GetSetMethod(true)))
        {
            return pi;
        }
    }
    throw new SomeException();
}

private static bool CheckMethod(MethodInfo method, MethodInfo propertyMethod) { 
    if (method == propertyMethod) {
        return true; 
    } 
    // If the type is an interface then the handle for the method got by the compiler will not be the
    // same as that returned by reflection. 
    // Check for this condition and try and get the method from reflection.
    Type type = method.DeclaringType;
    if (type.IsInterface && method.Name == propertyMethod.Name && type.GetMethod(method.Name) == propertyMethod) {
        return true; 
    }
    return false; 
} 

The problem is in this line:

if (method == propertyMethod) {
    return true; 
} 

When you get MethodInfo by GetGetMethod from Type A, it's different than MethodInfo from Type B, and this check returns false. Such example also returns false:

var propertyAGetter = typeof(A).GetProperty("MyString").GetGetMethod();
var propertyBGetter = typeof(B).GetProperty("MyString").GetGetMethod();
bool areTheSame = propertyAGetter == propertyBGetter; // it equals to false
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top