別の解決策は、他の言葉で、右の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
ではなく式を使用していますか?それから、あなたは次々に呼び出すことができます。発現木は後で解析されますか?
ありがとうございます!これをどのように最適化できますか?パフォーマンスはこの特定のケースでは非常に重要です。 –
私は答えを – LmTinyToon
に変更しました。2番目に提案されたソリューションは、パフォーマンスに関して非常に良いとは言えないデリゲートと匿名オブジェクトを作成します。 –