Reputation: 34832
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?
Upvotes: 3
Views: 2701
Reputation: 7458
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
Upvotes: 7