How to know when a lambda expression is null

I need to programmatically check if the result of a nested property / function is a lambda expression of zero or not. The problem is that zero can be in any of the nested sub-properties.

Example. Function:

public static bool HasNull<T, Y>(this T someType, Expression<Func<T, Y>> input) { //Determine if expression has a null property } 

Using:

 person.HasNull(d=>d.addressdetails.Street) person.HasNull(d=>d.addressdetails[1].Street) person.HasNull(d=>d.addressdetails.FirstOrDefault().Street) person.HasNull(d=>d.InvoiceList.FirstOrDefault().Product.Name) 

In either example, addressdetails or street, the invoicelist or the product or name may be null. The code throws an exception if I try to call a function, and some nested property is null.

Important: I do not want to use try catch for this, because it is not desirable for debugging performance.

The reason for this approach is the quick confirmation of the values, while I do not want to forget about any errors and therefore raise exceptions. This is convenient for reports on solutions and networks, where the zero in the report can simply be displayed empty and does not have additional business rules.

linked mail: Do not stop the debugger with this THAT exception when it is thrown and caught

+5
null c # lambda exception
Dec 24 '09 at 11:10
source share
6 answers

Maybe, but I'm not sure I will recommend it. Here's what you might find useful: it does not return a boolean, but instead the value of the expression sheet, if possible (without a null reference).

 public static class Dereferencer { private static readonly MethodInfo safeDereferenceMethodInfo = typeof (Dereferencer).GetMethod("SafeDereferenceHelper", BindingFlags.NonPublic| BindingFlags.Static); private static TMember SafeDereferenceHelper<TTarget, TMember>(TTarget target, Func<TTarget, TMember> walker) { return target == null ? default(TMember) : walker(target); } public static TMember SafeDereference<TTarget, TMember>(this TTarget target, Expression<Func<TTarget, TMember>> expression) { var lambdaExpression = expression as LambdaExpression; if (lambdaExpression == null) return default(TMember); var methodCalls = new Queue<MethodCallExpression>(); VisitExpression(expression.Body, methodCalls); var callChain = methodCalls.Count == 0 ? expression.Body : CombineMethodCalls(methodCalls); var exp = Expression.Lambda(typeof (Func<TTarget, TMember>), callChain, lambdaExpression.Parameters); var safeEvaluator = (Func<TTarget, TMember>) exp.Compile(); return safeEvaluator(target); } private static Expression CombineMethodCalls(Queue<MethodCallExpression> methodCallExpressions) { var callChain = methodCallExpressions.Dequeue(); if (methodCallExpressions.Count == 0) return callChain; return Expression.Call(callChain.Method, CombineMethodCalls(methodCallExpressions), callChain.Arguments[1]); } private static MethodCallExpression GenerateSafeDereferenceCall(Type targetType, Type memberType, Expression target, Func<ParameterExpression, Expression> bodyBuilder) { var methodInfo = safeDereferenceMethodInfo.MakeGenericMethod(targetType, memberType); var lambdaType = typeof (Func<,>).MakeGenericType(targetType, memberType); var lambdaParameterName = targetType.Name.ToLower(); var lambdaParameter = Expression.Parameter(targetType, lambdaParameterName); var lambda = Expression.Lambda(lambdaType, bodyBuilder(lambdaParameter), lambdaParameter); return Expression.Call(methodInfo, target, lambda); } private static void VisitExpression(Expression expression, Queue<MethodCallExpression> methodCallsQueue) { switch (expression.NodeType) { case ExpressionType.MemberAccess: VisitMemberExpression((MemberExpression) expression, methodCallsQueue); break; case ExpressionType.Call: VisitMethodCallExpression((MethodCallExpression) expression, methodCallsQueue); break; } } private static void VisitMemberExpression(MemberExpression expression, Queue<MethodCallExpression> methodCallsQueue) { var call = GenerateSafeDereferenceCall(expression.Expression.Type, expression.Type, expression.Expression, p => Expression.PropertyOrField(p, expression.Member.Name)); methodCallsQueue.Enqueue(call); VisitExpression(expression.Expression, methodCallsQueue); } private static void VisitMethodCallExpression(MethodCallExpression expression, Queue<MethodCallExpression> methodCallsQueue) { var call = GenerateSafeDereferenceCall(expression.Object.Type, expression.Type, expression.Object, p => Expression.Call(p, expression.Method, expression.Arguments)); methodCallsQueue.Enqueue(call); VisitExpression(expression.Object, methodCallsQueue); } } 

You can use it as follows:

 var street = person.SafeDereference(d=>d.addressdetails.Street); street = person.SafeDereference(d=>d.addressdetails[1].Street); street = person.SafeDereference(d=>d.addressdetails.FirstOrDefault().Street); var name = person.SafeDereference(d=>d.InvoiceList.FirstOrDefault().Product.Name); 

Warning: this is not fully tested, it should work with methods and properties, but probably not with extension methods inside an expression.

Edit: Ok, now it cannot handle extension methods (like FirstOrDefault ), but you can still customize the solution.

+3
Dec 24 '09 at 11:42
source share

You would need to split the expression and evaluate each bit in turn, stopping when you get a null result. That would be impossible, but there would be a lot of work.

Are you sure this works less than just putting explicit wrappers in your code?

+1
Dec 24 '09 at 11:19
source share

We definitely need a null-safe dereferencing operator in C #, but before that, see this question , which gives a slightly different, but also neat solution to the same problem.

+1
Dec 27 '09 at 10:18
source share

Any reason you could not just do the following?

 bool result; result = addressdetails != null && addressdetails.Street != null; result = addressdetails != null && addressdetails.Count > 1 && addressdetails[1].Street != null; result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Street != null; result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Product != null && addressdetails.FirstOrDefault().Product.Name != null; 

I think these simple Boolean expressions would be better ways, they work because of conditional evaluation ... When sharing (& &) terms, the first false term will return false and stop the rest from, so you won't get any exceptions that made it .

0
Dec 24 '09 at 11:21
source share

Although this is not the answer to this exact question, the easiest way I know to achieve the same behavior is to pass patches to properties as enumeration of property names instead of chains of calls.

 public static bool HasNull<T, Y>(this T someType, IEnumerable<string> propertyNames) 

Then analyze these enumerations in a circle or recursively using reflection. There are some disadvantages, such as loss of intelligence and static name checking, but very easy to implement, which in some cases can outweigh them.

Instead of recording

 person.HasNull(d=>d.addressdetails.FirstOrDefault().Street) 

you will have to write

 person.HasNull(new string[] { "addressdetails", "0", "Street" }) 
0
Dec 24 '09 at 11:38
source share

I wrote about blogs, it works in VB.NET (I translated into C #, but I have not tested the version of C #, check it out).

Checkout: How can you check car.Finish.Style.Year.Model.Vendor.Contacts.FirstOrDefault (). FullName for null? :-)

 Dim x = person.TryGetValue( Function(p) p.addressdetail(1).FirstOrDefault().Product.Name) 
0
Aug 11 '10 at 3:25
source share



All Articles