The main point here is that you do not need a "fully-created ifFalse statement", you can build it recursively.
The code might look like this:
public static Func<TO, TP> BuildSafeAccessor<TO, TP>(Expression<Func<TO, TP>> propertyExpression) { var properties = GetProperties(propertyExpression.Body); var parameter = propertyExpression.Parameters.Single(); var nullExpression = Expression.Constant(default(TP), typeof(TP)); var lambdaBody = BuildSafeAccessorExpression(parameter, properties, nullExpression); var lambda = Expression.Lambda<Func<TO, TP>>(lambdaBody, parameter); return lambda.Compile(); } private static Expression BuildSafeAccessorExpression(Expression init, IEnumerable<PropertyInfo> properties, Expression nullExpression) { if (!properties.Any()) return init; var propertyAccess = Expression.Property(init, properties.First()); var nextStep = BuildSafeAccessorExpression(propertyAccess, properties.Skip(1), nullExpression); return Expression.Condition( Expression.ReferenceEqual(init, Expression.Constant(null)), nullExpression, nextStep); } private static IEnumerable<PropertyInfo> GetProperties(Expression expression) { var results = new List<PropertyInfo>(); while (expression is MemberExpression) { var memberExpression = (MemberExpression)expression; results.Add((PropertyInfo)memberExpression.Member); expression = memberExpression.Expression; } if (!(expression is ParameterExpression)) throw new ArgumentException(); results.Reverse(); return results; }
(Note that this code uses LINQ inefficiently to make it more readable.)
If you run it on lambda a => aBCD , it will create an expression (showing the result of ToString() ):
a => IIF((a == null), null, IIF((aB == null), null, IIF((aBC == null), null, aBCD)))
Please note that this allows access to aB up to three times. If this property is slow or has side effects, this can be a problem. If this is a problem for you, I think you will need to use Block with local variables.
svick source share