I'm trying to determine the MemberExpression of all the parts of a structure with the use of reflection. These are some of the objects to illustrate the issue:
public class Entity
{
public Part FirstPart { get; set; }
}
public class Part
{
public int Id { get; set; }
}
public class SubPart : Part
{
public int ExtraProperty { get; set; }
}
The function I used to determine the MemberExpression of every component, works fine for the following object structure:
Entity entity = new Entity() { FirstPart = new Part() { Id = 1 } };
The function is:
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.Id").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
But this doesn't work for the following, due to inheritance:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
In order to retrace the properties we need to change the path to "FirstPart.ExtraProperty":
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
The error message states that: 'ExtraProperty' is not a member of Part. Does anyone have an idea how to overcome this issue?
You can't. Think of expressions as code, which is compiled at runtime instead of compile time. There is no magic and similar rules apply (expressions are low level and more restrictive, so many syntactic sugar which are available at C# code level are not available in expressions). Saying that, since entity.FirstPart.ExtraProperty
is not valid in C# code, it won't be valid in expressions as well.
You could insert explicit cast - but then you assume that instance will actually be of type SubPart
, so why don't you define member FirstPart
of type SubPart
instead of Part
. Or you could create type test logic using TypeIs expression and then cast the same way as you would in C# code.
EDIT:
After rereading your problem, I see that what you actually are trying to implement is property walker over arbitrary objects. So TypeIs
expression will not help you here since it requires that type, which you are testing against, is known at compile time. But in your case there can be arbitrary class derived from Part
in FirstPart
member with arbitrary additional properties. In this case, there is no other option, but to evaluate each property access one by one and retrieve actual type from intermediate values. For example:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
object currentObjectInChain = entity;
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
foreach (var property in childProperties)
{
if (currentObjectInChain == null)
{
throw new ArgumentException("Current value is null");
}
var type = currentObjectInChain.GetType();
var param = Expression.Parameter(type);
var lambda = Expression.Lambda(
Expression.PropertyOrField(param, property),
param).Compile(); // cache based on type and property name
currentObjectInChain = lambda.DynamicInvoke(currentObjectInChain);
}
At the end of loop currentObjectInChain
will hold your value.