ここで重要なのは、「ifFalse式はすでに完全に作成されている」必要はなく、再帰的に作成できるということです。
コードは次のようになります。
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;
}
(このコードは、読みやすくするためにLINQを非効率的に使用していることに注意してください。)
ラムダで実行するとa => a.B.C.D
、式が作成されます(その結果が表示ToString()
されます)。
a => IIF((a == null), null, IIF((a.B == null), null, IIF((a.B.C == null), null, a.B.C.D)))
a.B
これは最大3回アクセスすることに注意してください。そのプロパティが遅いか、副作用がある場合は、問題になる可能性があります。Block
これが問題になる場合は、ローカル変数でsを使用する必要があると思います。