3

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?

Haney
  • 32,775
  • 8
  • 59
  • 68

1 Answers1

7

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
Sergey Litvinov
  • 7,408
  • 5
  • 46
  • 67
  • 2
    This worked, thank you! I'll give you the answer if you can tell me WHY it worked because I don't understand and it kills me not to understand my code! – Haney Jan 13 '14 at 16:45
  • 1
    added my guess into answer :) – Sergey Litvinov Jan 13 '14 at 16:55
  • 1
    Damn, great answer. Thank you @Sergey - really appreciate it. I wonder if this is technically a bug in the .NET framework? – Haney Jan 13 '14 at 17:14
  • 1
    technically these two objects are different because of ReflectedType. So, it's not a bug. Here is an answer about ReflectedType given by Jon Skeet - http://stackoverflow.com/questions/16276271/why-is-a-member-of-base-class-different-from-the-same-member-in-derived-class – Sergey Litvinov Jan 13 '14 at 17:18
  • 1
    I disagree with their design decision regarding `ReflectedType`, but at least now I understand why my code was breaking. :) – Haney Jan 13 '14 at 17:25
  • 2
    It still seems like a bug that `Expression.Property` accepts a `PropertyInfo` with a derived `ReflectedType`, but not a `MethodInfo` with one. – nmclean Jan 13 '14 at 17:31
  • @nmclean I agree. Seems wrong but maybe there's a use case we aren't thinking of. – Haney Jan 13 '14 at 17:35