4

私の目標は、Lambdasを使用して、深いプロパティ値を安全に取得できるプロパティバインディングオブジェクトを作成することです。安全のため、以前のプロパティの1つがnullの場合、null参照例外をスローするのではなく、プロパティタイプのデフォルト値を返します。

メソッドシグネチャ:

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

*編集:私の質問を明確にする

だから私が呼ぶなら:

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

safeAccessorが呼び出されると、そのロジックは次のようになります。

if (p.Address == null)
    return default(TP);
return p.Address.Zip;
4

1 に答える 1

3

ここで重要なのは、「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を使用する必要があると思います。

于 2013-01-17T16:27:31.807 に答える