2 つの「セレクター」式が与えられた場合、それらのバインディングを取得し、MemberInitExpression
すべてのバインディングを使用して新しい式を作成する必要があります。ただし、この式は 1 つのパラメーターに対して 2 つの異なるパラメーター式を使用するため、機能しません。それも修正する必要があります。
与えられた...
Expression<Func<TSource, TResult>> left = ... // columns
Expression<Func<TSource, TResult>> right = ... // more columns
...バインディングを取ります...
var leftInit = left.Body as MemberInitExpression;
var rightInit = right.Body as MemberInitExpression;
var bindings = leftInit.Bindings.Concat(rightInit.Bindings);
...新しい表現を作成...
var result = Expression.Lambda<Func<TSource, TResult>>(
Expression.MemberInit(Expression.New(typeof(TResult)), bindings), ???);
...しかし、単一のパラメーターが必要です...
var binder = new ParameterBinder(left.Parameters[0], right.Parameters[0]);
var bindings = binder.Visit(leftInit.Bindings.Concat(rightInit.Bindings));
// now, just use right.Parameters[0] as parameter...
また、パラメーターの置換は、式ビジターを使用してうまく機能します。
class ParameterBinder : ExpressionVisitor
{
readonly ParameterExpression parameter;
readonly Expression replacement;
public ParameterBinder(ParameterExpression parameter, Expression replacement)
{
this.parameter = parameter;
this.replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == parameter)
return replacement;
return base.VisitParameter(node);
}
}
この配管を抽象化することは非常にうまく機能します。実際、既存のライブラリをそのまま使用できます(ネタバレ: 私は作成者です)。
var merged = columns.Apply(moreColumns);