Create Lambda's secure deep property accessory with expressions

My goal is to use Lambdas to create a property binding object that can safely retrieve the deep value of a property. In safe mode, it returns the default value for the property type if one of the previous properties is null, and does not exclude the exception of the reference.

Method Signature:

public static Func<TO, TP> BuildSafeAccessor<TO, TP>(this Expression<Func<TO, TP>> propertyExpression) where TO: class { } 

* Edit: Refine my question

So if I call:

 var safeAccessor = BuildSafeAccessor<Person>(p => p.Address.Zip); 

When safeAccessor is called, the logic will be as follows:

 if (p.Address == null) return default(TP); return p.Address.Zip; 
+4
source share
1 answer

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.

+3
source

All Articles