133

メソッド呼び出しを使用すると、ラムダから式に簡単に移行できます...

public void GimmeExpression(Expression<Func<T>> expression)
{
    ((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}

public void SomewhereElse()
{
    GimmeExpression(() => thing.DoStuff());
}

しかし、まれにのみ Func を式に変換したいと思います...

public void ContainTheDanger(Func<T> dangerousCall)
{
    try 
    {
        dangerousCall();
    }
    catch (Exception e)
    {
        // This next line does not work...
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

動作しない行により、コンパイル時エラーが発生しますCannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'。明示的なキャストは状況を解決しません。私が見落としているこれを行うための施設はありますか?

4

9 に答える 9

112

ああ、それはまったく簡単ではありません。Func<T>式ではなくジェネリックdelegateを表します。できる方法があるとすれば (最適化やコンパイラによるその他の処理により、一部のデータが破棄される可能性があるため、元の式を取り戻すことが不可能になる可能性があります)、その場で IL を逆アセンブルすることです。式を推論します (決して簡単ではありません)。ラムダ式をデータ ( ) として扱うのは、コンパイラーExpression<Func<T>>によって行われる魔法です(基本的に、コンパイラーは、コードを IL にコンパイルするのではなく、コードで式ツリーを構築します)。

関連事実

これが、(Lisp のように) ラムダを極端に押し上げる言語が、多くの場合、インタープリターとして実装するのがより簡単である理由です。これらの言語では、コードとデータは (実行時であっても) 基本的に同じものですが、私たちのチップはその形式のコードを理解できないため、その上にそれを理解するインタープリターを構築して、そのようなマシンをエミュレートする必要があります (言語のように Lisp によってなされた選択) または能力をある程度犠牲にする (コードはもはやデータと完全に等しくならない) (C# によってなされた選択)。C# では、コンパイラは、コンパイル時にラムダをcode ( Func<T>) およびdata ( Expression<Func<T>>)として解釈できるようにすることで、コードをデータとして扱っているような錯覚を与えます。

于 2009-04-20T10:43:10.077 に答える
43
    private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)  
    {  
        return x => f(x);  
    } 
于 2011-02-27T13:48:59.180 に答える
25

おそらくすべきことは、メソッドを変えることです。Expression> を取り込み、コンパイルして実行します。失敗した場合は、調査する式が既にあります。

public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
    try 
    {
        dangerousCall().Compile().Invoke();;
    }
    catch (Exception e)
    {
        // This next line does not work...
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

明らかに、これがパフォーマンスに与える影響を考慮し、本当に実行する必要があるかどうかを判断する必要があります。

于 2009-04-20T10:53:59.567 に答える
10

NJection.LambdaConverterはデリゲートを式に変換するライブラリです

public class Program
{
    private static void Main(string[] args) {
       var lambda = Lambda.TransformMethodTo<Func<string, int>>()
                          .From(() => Parse)
                          .ToLambda();            
    }   
        
    public static int Parse(string value) {
       return int.Parse(value)
    } 
}
于 2012-09-02T17:29:07.073 に答える
8

ただし、 .Compile() メソッドを介して別の方法に進むことができます-これが役立つかどうかはわかりません:

public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
    try
    {
        var expr = dangerousCall.Compile();
        expr.Invoke();
    }
    catch (Exception e)
    {
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
        throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    var thing = new Thing();
    ContainTheDanger(() => thing.CrossTheStreams());
}
于 2009-04-20T10:55:39.627 に答える
8

式が必要な場合とデリゲートが必要な場合がある場合は、次の 2 つのオプションがあります。

  • さまざまな方法があります(それぞれに1つ)
  • 常にExpression<...>バージョンを受け入れ、.Compile().Invoke(...)デリゲートが必要な場合はそれを受け入れます。明らかにこれにはコストがかかります。
于 2009-04-20T10:52:58.383 に答える
6
 Expression<Func<T>> ToExpression<T>(Func<T> call)
        {
            MethodCallExpression methodCall = call.Target == null
                ? Expression.Call(call.Method)
                : Expression.Call(Expression.Constant(call.Target), call.Method);

            return Expression.Lambda<Func<T>>(methodCall);
        }
于 2013-03-21T12:38:54.810 に答える
3

Cecil Mono チームの JB Evain は、これを可能にするためにいくつかの進歩を遂げています。

http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees

于 2009-05-01T19:38:41.277 に答える