5

2 つの LambdaExpressions をコンパイルせずに結合したい。

これをコンパイルすると、次のようになります。

    public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
        Expression<Func<TContainer,TMember>> getMemberExpression, 
        Expression<Func<TMember,bool>> memberPredicateExpression)
    {
        return x => memberPredicateExpression.Compile()(getMemberExpression.Compile()(x));
    }

これは明らかに、提供された引数からターゲット式を取得する最速の方法ではありません。また、C# メソッド呼び出しをサポートしない LINQ to SQL などのクエリ プロバイダーとの互換性がなくなります。

私が読んだことから、最良のアプローチはExpressionVisitorクラスを構築することのようです。ただし、これはかなり一般的なタスクのようです。この種の機能を提供する既存のオープン ソース コード ベースを知っている人はいますか? ExpressionVisitorそうでない場合、可能な限り一般的なものにするためにアプローチする最良の方法は何ですか?

4

1 に答える 1

4

それが最善の方法かどうかはわかりませんが、次のようなことができます。

public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
    Expression<Func<TContainer,TMember>> getMemberExpression, 
    Expression<Func<TMember,bool>> memberPredicateExpression)
{
    ParameterExpression x = Expression.Parameter(typeof(TContainer), "x");
    return Expression.Lambda<Func<TContainer, bool>>(
        Expression.Invoke(
            memberPredicateExpression,
            Expression.Invoke(
                getMemberExpression,
                x)),
        x);
}

使用法:

var expr = CreatePredicate(
    (Foo f) => f.Bar,
    bar => bar % 2 == 0);

結果:

x => Invoke(bar => ((bar % 2) == 0), Invoke(f => f.Bar, x))

のようなものを手に入れたほうがいいと思いますがx => x.Bar % 2 == 0、おそらくかなり難しいでしょう...


編集:実際には、式ビジターではそれほど難しくありませんでした:

public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
    Expression<Func<TContainer,TMember>> getMemberExpression, 
    Expression<Func<TMember,bool>> memberPredicateExpression)
{
    return CombineExpressionVisitor.Combine(
        getMemberExpression,
        memberPredicateExpression);
}

class CombineExpressionVisitor : ExpressionVisitor
{
    private readonly ParameterExpression _parameterToReplace;
    private readonly Expression _replacementExpression;
    private CombineExpressionVisitor(ParameterExpression parameterToReplace, Expression replacementExpression)
    {
        _parameterToReplace = parameterToReplace;
        _replacementExpression = replacementExpression;
    }

    public static Expression<Func<TSource, TResult>> Combine<TSource, TMember, TResult>(
        Expression<Func<TSource, TMember>> memberSelector,
        Expression<Func<TMember, TResult>> resultSelector)
    {
         var visitor = new CombineExpressionVisitor(
            resultSelector.Parameters[0],
            memberSelector.Body);
        return Expression.Lambda<Func<TSource, TResult>>(
            visitor.Visit(resultSelector.Body),
            memberSelector.Parameters);
    }

    protected override Expression VisitParameter(ParameterExpression parameter)
    {
        if (parameter == _parameterToReplace)
            return _replacementExpression;
        return base.VisitParameter(parameter);
    }
}

次の式が得られます。

f => ((f.Bar % 2) == 0)
于 2011-03-19T12:42:59.213 に答える