Say you are trying to read this property
var town = Staff.HomeAddress.Postcode.Town;
Somewhere along the chain a null could exist. What wo
I agree with Oded that this violates the Law of Demeter.
I was intrigued by your question though, so I wrote up a a poor man's "Null-Safe Evaluate" extension-method with expression-trees, just for fun. This should give you compact syntax to express the desired semantics.
Please don't use this in production code.
Usage:
var town = Staff.NullSafeEvaluate(s => s.HomeAddress.Postcode.Town);
This will evaluate in succession:
Staff
Staff.HomeAddress
Staff.HomeAddress.Postcode
Staff.HomeAddress.Postcode.Town
(Caching and reusing the values of the intermediate expressions to produce the next one)
If it encounters a null reference, it returns the default value of the type of Town. Otherwise, it returns the value of the full expression.
(Not throughly tested, can be improved in terms of performance and doesn't support instance-methods. POC only.)
public static TOutput NullSafeEvaluate
(this TInput input, Expression> selector)
{
if (selector == null)
throw new ArgumentNullException("selector");
if (input == null)
return default(TOutput);
return EvaluateIterativelyOrDefault
(input, GetSubExpressions(selector));
}
private static T EvaluateIterativelyOrDefault
(object rootObject, IEnumerable expressions)
{
object currentObject = rootObject;
foreach (var sourceMemEx in expressions)
{
// Produce next "nested" member-expression.
// Reuse the value of the last expression rather than
// re-evaluating from scratch.
var currentEx = Expression.MakeMemberAccess
(Expression.Constant(currentObject), sourceMemEx.Member);
// Evaluate expression.
var method = Expression.Lambda(currentEx).Compile();
currentObject = method.DynamicInvoke();
// Expression evaluates to null, return default.
if (currentObject == null)
return default(T);
}
// All ok.
return (T)currentObject;
}
private static IEnumerable GetSubExpressions
(Expression> selector)
{
var stack = new Stack();
var parameter = selector.Parameters.Single();
var currentSubEx = selector.Body;
// Iterate through the nested expressions, "reversing" their order.
// Stop when we reach the "root", which must be the sole parameter.
while (currentSubEx != parameter)
{
var memEx = currentSubEx as MemberExpression;
if (memEx != null)
{
// Valid member-expression, push.
stack.Push(memEx);
currentSubEx = memEx.Expression;
}
// It isn't a member-expression, it must be the parameter.
else if (currentSubEx != parameter)
{
// No, it isn't. Throw, don't support arbitrary expressions.
throw new ArgumentException
("Expression not of the expected form.", "selector");
}
}
return stack;
}