Monday, October 11, 2010

The vassal of my vassal is not my vassal

Just a thumb rule - avoid creating a PropertyExpression using

public static MemberExpression Expression.Property(Expression expression, MethodInfo property)

Instead prefer

public static MemberExpression Expression.Property(Expression expression, PropertyInfo property)



Consider the following example:
public class Base
{
  public int BaseProperty { get; set; }
}
public class Derived : Base
{
  public int DerivedProperty { get; set; }
}

static void Main()
{
  Base b = new Base();
  Derived d = new Derived();
  Base bd = d;

  var parameter = Expression.Parameter(typeof(Derived), "d");
  Console.WriteLine(GetProperty(parameter, b, "BaseProperty"));
  Console.WriteLine(GetProperty(parameter, bd, "BaseProperty"));
  Console.WriteLine(GetProperty(parameter, d, "BaseProperty"));
}

static Expression GetProperty<T>(Expression instance, T entity, string propertyName)
{
  return Expression.Property(instance, typeof(T).GetProperty(propertyName).GetGetMethod());
}

First two Console.WriteLine will proceed, but the third one will fail with
System.ArgumentException: The method 'Program+Base.get_BaseProperty' is not a property accessor
The answer obviously is inside Expression.Property method. The method tries to find the corresponding PropertyInfo by comparing the provided MethodInfo and either a getter or a setter of any property in the declared type of a provided MethodInfo. Unfortunately, there is a trap. Consider the following example:

var property = typeof(Derived).GetProperty("BaseProperty");
var m1 = property.GetGetMethod();
var m2 = property.DeclaringType.GetProperty("BaseProperty").GetGetMethod();
Console.WriteLine(m1.Equals(m2));


The Console.WriteLine() will out false, because m1 and m2 will be actually different objects.

No comments:

Post a Comment