When performing a Select
on an IEnumerable
I believe it is good practice to check for null references, so I often have a Where
before my Select
like this:
someEnumerable.Where(x => x != null).Select(x => x.SomeProperty);
This gets more complicated when accessing sub-properties:
someEnumerable.Where(x => x != null && x.SomeProperty != null).Select(x => x.SomeProperty.SomeOtherProperty);
To follow this pattern I need to do a lof of calls to Where
. I would like to create an extension method on IEnumerable
that perform such null-checks automatically depending on what is referenced in the Select
. Like this:
someEnumerable.SelectWithNullCheck(x => x.SomeProperty);
someEnumerable.SelectWithNullCheck(x => x.SomeProperty.SomeOtherProperty);
Can this be done? Is it fx. possible to retrieve the selected properties from the selector
parameter when creating an extension method such as this?
public static IEnumerable<TResult> SelectWithNullCheck<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
return source.Where(THIS IS WHERE THE AUTOMATIC NULL-CHECKS HAPPEN).Select(selector);
}
EDIT: I use C# 5.0 with .NET Framework 4.5
As you are using C# 5.0 you can write your extension method the following way:
public static IEnumerable<TResult> SelectWithNullCheck<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
return source.Where(x => x != null).Select(selector).Where(x => x != null);
}
Before and after the projection (Select call)
to apply a check that the result is not null.
Then usage will be:
someEnumerable.SelectWithNullCheck(x => x.SomeProperty)
.SelectWithNullCheck(y => y.SomeOtherProperty);
Notice that the type of item in each call is different.
If you do want it similar to this:
someEnumerable.SelectWithNullCheck(x => x.SomeProperty.SomeOtherProperty);
Then you'd need to go with @Treziac's suggestion and use the ?.
operator (introduced in C# 6.0) and then filter nulls out:
public static IEnumerable<TResult> SelectWithNullCheck<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
return source.Select(selector).Where( x=> x != null);
}
someEnumerable.SelectWithNullCheck(x => x?.SomeProperty?.SomeOtherProperty);
You can use Expression based solution. Below is basic and workable solution for field / property chain call. It will work for very deep call chains. It is not perfect. For example it won't work if there is method call in the chain (obj.Prop1.MethodCall().Prop2).
Expression based solutions are in general slower, because of the need to compile the lambda expression to delegate, which should be taken in consideration.
Performance stats:
Testes with collection of 200k objects with nested call level of 2 (obj.Prop1.Prop2) where all objects fail for the condition.
LINQ Where with C# 6 ?. operator : 2 - 4 ms
Exception based (try / catch): 14,000 - 15,000 ms
Expression based: 4 - 10 ms
NOTE: The expression based solution will add overhead of several ms for every call, this number won't depend on the collection size, because the expression will be compiled for every call which is an expensive operation. You can think for cache mechanism if you are interested.
Source for expression based solution::
public static IEnumerable<T> IgnoreIfNull<T, TProp>(this IEnumerable<T> sequence, Expression<Func<T, TProp>> expression)
{
var predicate = BuildNotNullPredicate(expression);
return sequence.Where(predicate);
}
private static Func<T, bool> BuildNotNullPredicate<T, TProp>(Expression<Func<T, TProp>> expression)
{
var root = expression.Body;
if (root.NodeType == ExpressionType.Parameter)
{
return t => t != null;
}
var pAccessMembers = new List<Expression>();
while (root.NodeType == ExpressionType.MemberAccess)
{
var mExpression = root as MemberExpression;
pAccessMembers.Add(mExpression);
root = mExpression.Expression;
}
pAccessMembers.Reverse();
var body = pAccessMembers
.Aggregate(
(Expression)Expression.Constant(true),
(f, s) =>
{
if (s.Type.IsValueType)
{
return f;
}
return Expression.AndAlso(
left: f,
right: Expression.NotEqual(s, Expression.Constant(null))
);
});
var lambda = Expression.Lambda<Func<T, bool>>(body, expression.Parameters[0]);
var func = lambda.Compile();
return func;
}
This is how is used:
var sequence = ....
var filtered = sequence.IgnoreIfNull(x => x.Prop1.Prop2.Prop3 ... etc);
Why not using ?.
operator ?
someEnumerable.Where(x => x?.SomeProperty != null).Select(x => x.SomeProperty.SomeOtherProperty);
(note that this may return null values)
or
someEnumerable.Select(x => x?.SomeProperty?.SomeOtherProperty).Where(x => x != null);
(this will not return any null values)
It's not really good or bad practice, it depends of what you want in your return
Another option is to split the selection null check into a custom operator (e.g. WhereNotNull
). Combine this with the ?.
operator, solves your problem in an imho very expressive way.
public static IEnumerable<TSource> WhereNotNull<TSource>(this IEnumerable<TSource> source)
{
return source.Where(x=> x != null);
}
This allows you to write:
someEnumerable.Select(x => x?.SomeProperty?.SomeOtherProperty)
.WhereNotNull();
If not you could always chain the selects
(for version prior to C# 6):
someEnumerable.Select(x => x.SomeProperty)
.Select(x => x.SomeOtherProperty)
.WhereNotNull();
Given you absolutely want to access x.SomeProperty.SomeOtherProperty
the last option would be to catch the NullReferenceException
.
public static IEnumerable<TResult> SelectWithNullCheck<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
return source.Select(x =>
{
try
{
return selector(x);
}
catch(NullReferenceException ex)
{
return default(TResult);
}
})
.Where(x=> default(TResult) != x);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With