2016-11-19 14 views
3

に2つの式を組み合わせることで、私たちは、私は、次の2つの式を持っているとしましょう:パイプライン

Expression<Func<T, IEnumerable<TNested>>> collectionSelector; 
Expression<Func<IEnumerable<TNested>, TNested>> elementSelector; 

は以下を形成するために、これらを「組み合わせる」ことがある方法です:

Expression<Func<T, TNested>> selector; 
(?)

EDIT:

パフォーマンスは非常に重要なので、できるだけオーバーヘッドの少ない最適なソリューションをお試しください。

多くのありがとうございます!

答えて

4
static Expression<Func<A, C>> Foo<A, B, C>(
Expression<Func<B, C>> f, 
Expression<Func<A, B>> g) 
{ 
    var x = Expression.Parameter(typeof(A)); 
    return Expression.Lambda<Func<A, C>>(
     Expression.Invoke(f, Expression.Invoke(g, x)), x); 
} 

残念ながら、私はコンピュータにアクセスできません(パフォーマンス面では悪い解決策です)。実際には、私はあなたが呼び出し式を介してコードを最適化できると思う。

もう一つの方法は、(使用補助機能)のように思える:

Func<A, C> Foo<A,B,C>(Func<B, C> f, Func<A, B> g) 
{ 
    return (A x) => f(g(x)); 
} 

次に、あなたがfoo関数を呼び出し式を経てパイプライン式を作成する必要があります。その擬似コードのように:

var expr1 = get expresstion<B,C> 
    var expr2 = get Expression<A, B> 
    var foo = get method info of Foo method 
    specialize method with generic types A, B, C (via make generic method) 
    return Expression.Call(foo, expr1, expr2); 
+0

ありがとうございます!これをどのように最適化できますか?パフォーマンスはこの特定のケースでは非常に重要です。 –

+0

私は答えを – LmTinyToon

+0

に変更しました。2番目に提案されたソリューションは、パフォーマンスに関して非常に良いとは言えないデリゲートと匿名オブジェクトを作成します。 –

1

別の解決策は、他の言葉で、右の1で左に1を埋め込む、全体の左側の式と右側の式でパラメータを置き換えるためにExpressionVisitorを使用することです。

エクスプレッションビジターは非常にシンプルになり、必要なデータをコンストラクタに追加して1つのメソッドをオーバーライドするだけです。

internal sealed class ParameterReplaceVisitor : ExpressionVisitor 
{ 
    private readonly ParameterExpression _searched; 
    private readonly Expression _replaced; 

    public ParameterReplaceVisitor(ParameterExpression searched, Expression replaced) 
    { 
     if (searched == null) 
      throw new ArgumentNullException(nameof(searched)); 
     if (replaced == null) 
      throw new ArgumentNullException(nameof(replaced)); 

     _searched = searched; 
     _replaced = replaced; 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (node == _searched) 
      return _replaced; 

     return base.VisitParameter(node); 
    } 
} 

これは、コンストラクタ内の式のコレクションを処理するために非常に簡単に拡張できますが、私はそれを省略しました。

これで、式のボディでそれを使用し、新しいラムダを構築するだけです。

private static Expression<Func<TIn, TOut>> Merge<TIn, TInter, TOut>(Expression<Func<TIn, TInter>> left, Expression<Func<TInter, TOut>> right) 
{ 
    var merged = new ParameterReplaceVisitor(right.Parameters[0], left.Body).Visit(right.Body); 

    var lambda = Expression.Lambda<Func<TIn, TOut>>(merged, left.Parameters[0]); 

    return lambda; 
} 

私はこのコードでそれをテストした:

Expression<Func<string, int>> l = s => s.Length + 5; 
Expression<Func<int, string>> r = i => i.ToString() + " something"; 

var merged = Merge(l, r); 

var res = merged.Compile()("test"); 

と期待通りの結果は次のとおりです。9 something

EDIT: パフォーマンスが懸念される場合は、なぜ普通のFuncではなく式を使用していますか?それから、あなたは次々に呼び出すことができます。発現木は後で解析されますか?

+0

私はこのソリューションがなぜUPでないのかわかりません。これだよ!これは完璧です!ありがとうございました。 – Gh61