Lookup property in object graph via a string
I've had to some something similar in the past. I went with the lambda approach because after compiling them I can cache them. I've removed the caching in this code.
I included a few unit tests to show the usage of the method. I hope this is helpful.
private static object GetValueForPropertyOrField( object objectThatContainsPropertyName, IEnumerable<string> properties )
{
foreach ( var property in properties )
{
Type typeOfCurrentObject = objectThatContainsPropertyName.GetType();
var parameterExpression = Expression.Parameter( typeOfCurrentObject, "obj" );
Expression memberExpression = Expression.PropertyOrField( parameterExpression, property );
var expression = Expression.Lambda( Expression.GetDelegateType( typeOfCurrentObject, memberExpression.Type ), memberExpression, parameterExpression ).Compile();
objectThatContainsPropertyName = expression.DynamicInvoke( objectThatContainsPropertyName );
}
return objectThatContainsPropertyName;
}
[TestMethod]
public void TestOneProperty()
{
var dateTime = new DateTime();
var result = GetValueForPropertyOrField( dateTime, new[] { "Day" } );
Assert.AreEqual( dateTime.Day, result );
}
[TestMethod]
public void TestNestedProperties()
{
var dateTime = new DateTime();
var result = GetValueForPropertyOrField( dateTime, new[] { "Date", "Day" } );
Assert.AreEqual( dateTime.Date.Day, result );
}
[TestMethod]
public void TestDifferentNestedProperties()
{
var dateTime = new DateTime();
var result = GetValueForPropertyOrField( dateTime, new[] { "Date", "DayOfWeek" } );
Assert.AreEqual( dateTime.Date.DayOfWeek, result );
}
Here's a non-recursive version with (almost) the same semantics:
private static object ObjectFromString(object basePoint, IEnumerable<string> pathToSearch)
{
var value = basePoint;
foreach (var propertyName in pathToSearch)
{
var property = value.GetType().GetProperty(propertyName);
if (property == null) return null;
value = property.GetValue(value, null);
}
return value;
}
You could simply use the standard .NET DataBinder.Eval Method, like this:
object result = DataBinder.Eval(myPerson, "PersonsAddress.HousePhone.Number");