12

C# の式クラスを使用して Xml を解析して作成した式ツリーがあります。この質問を参照してください

Expression Tree には、Add、Subtract、Divide、Multiply、Parameters、および And と Or しかありません。この ExpressionTree を呼び出し可能なメソッドに変換する方法はありますか? ...または、IL を手動で発行する必要がありますか?

敬具、

4

2 に答える 2

12

ラムダを作成する必要があります-つまり

var lambda = Expression.Lambda<Func<float,int>>(body, param);
Func<float,int> method = lambda.Compile();
int v = method(1.0); // test

ここで、「body」は、パラメーターを含む式ツリー (float を取り、int を返す)ParameterExpressionです。

thisおよびthisも役立つ場合があります。

于 2008-12-06T17:52:09.823 に答える
10

両方のアプローチの例を次に示します。私が何かを見逃した場合、またはより多くの情報が必要な場合は、お知らせください。

static void Main()
{
    // try to do "x + (3 * x)"

    var single = BuildSingle<decimal>();
    var composite = BuildComposite<decimal>();

    Console.WriteLine("{0} vs {1}", single(13.2M), composite(13.2M));
}
// utility method to get the 3 as the correct type, since there is not always a "int x T"
static Expression ConvertConstant<TSource, TDestination>(TSource value)
{
    return Expression.Convert(Expression.Constant(value, typeof(TSource)), typeof(TDestination));
}
// option 1: a single expression tree; this is the most efficient
static Func<T,T> BuildSingle<T>()
{        
    var param = Expression.Parameter(typeof(T), "x");
    Expression body = Expression.Add(param, Expression.Multiply(
        ConvertConstant<int, T>(3), param));
    var lambda = Expression.Lambda<Func<T, T>>(body, param);
    return lambda.Compile();
}
// option 2: nested expression trees:
static Func<T, T> BuildComposite<T>()
{

    // step 1: do the multiply:
    var paramInner = Expression.Parameter(typeof(T), "inner");
    Expression bodyInner = Expression.Multiply(
        ConvertConstant<int, T>(3), paramInner);
    var lambdaInner = Expression.Lambda(bodyInner, paramInner);

    // step 2: do the add, invoking the existing tree
    var paramOuter = Expression.Parameter(typeof(T), "outer");
    Expression bodyOuter = Expression.Add(paramOuter, Expression.Invoke(lambdaInner, paramOuter));
    var lambdaOuter = Expression.Lambda<Func<T, T>>(bodyOuter, paramOuter);

    return lambdaOuter.Compile();
}

個人的には、最初の方法を目指します。よりシンプルで効率的です。これには、ネストされたコードのスタック全体に元のパラメーターを渡すことが含まれる場合がありますが、それも同様です。「呼び出し」アプローチ(複合)を使用し、最初のアプローチ(単一)としてツリーを書き直すコードをどこかに持っていますが、それは非常に複雑で長いです。ただし、Entity Framework (Expression.Invoke をサポートしていない) には非常に便利です。

于 2008-12-06T22:21:42.540 に答える