J'ai été inspiré par cette question pour essayer de trouver comment ce type de vérification profonde des nullités peut être réalisé avec une syntaxe plus facile/plus jolie en utilisant des arbres d'expression. Bien que je sois d'accord avec les réponses indiquant qu'il est possible d'utiliser des arbres d'expression pour la vérification des nullités. pourrait est une mauvaise conception si vous avez souvent besoin d'accéder à des instances situées au plus profond de la hiérarchie, je pense également que dans certains cas, comme la présentation de données, cela peut être très utile.
J'ai donc créé une méthode d'extension, qui vous permettra d'écrire :
var berries = cake.IfNotNull(c => c.Frosting.Berries);
Cela renverra les baies si aucune partie de l'expression n'est nulle. Si null est rencontré, null est retourné. Il y a cependant quelques réserves : dans la version actuelle, elle ne fonctionne qu'avec un accès simple aux membres, et elle ne fonctionne que sur .NET Framework 4, car elle utilise la méthode MemberExpression.Update, qui est nouvelle dans la version 4. Voici le code de la méthode d'extension IfNotNull :
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace dr.IfNotNullOperator.PoC
{
public static class ObjectExtensions
{
public static TResult IfNotNull<TArg,TResult>(this TArg arg, Expression<Func<TArg,TResult>> expression)
{
if (expression == null)
throw new ArgumentNullException("expression");
if (ReferenceEquals(arg, null))
return default(TResult);
var stack = new Stack<MemberExpression>();
var expr = expression.Body as MemberExpression;
while(expr != null)
{
stack.Push(expr);
expr = expr.Expression as MemberExpression;
}
if (stack.Count == 0 || !(stack.Peek().Expression is ParameterExpression))
throw new ApplicationException(String.Format("The expression '{0}' contains unsupported constructs.",
expression));
object a = arg;
while(stack.Count > 0)
{
expr = stack.Pop();
var p = expr.Expression as ParameterExpression;
if (p == null)
{
p = Expression.Parameter(a.GetType(), "x");
expr = expr.Update(p);
}
var lambda = Expression.Lambda(expr, p);
Delegate t = lambda.Compile();
a = t.DynamicInvoke(a);
if (ReferenceEquals(a, null))
return default(TResult);
}
return (TResult)a;
}
}
}
Il fonctionne en examinant l'arbre d'expression représentant votre expression, et en évaluant les parties l'une après l'autre ; chaque fois, il vérifie que le résultat n'est pas nul.
Je suis sûr que cela pourrait être étendu afin que d'autres expressions que MemberExpression soient supportées. Considérez ceci comme un code de preuve de concept, et s'il vous plaît gardez à l'esprit qu'il y aura une pénalité de performance en l'utilisant (qui n'aura probablement pas d'importance dans de nombreux cas, mais ne l'utilisez pas dans une boucle serrée :-)) )