C# C“4”;“动态”;在表达式树中

C# C“4”;“动态”;在表达式树中,c#,dynamic,c#-4.0,expression-trees,C#,Dynamic,C# 4.0,Expression Trees,我正试图找出如何把所有的部分放在一起,如果能从一个简单的案例开始,我希望有一个具体的源代码示例 考虑以下C#代码: Func f=(x,y)=>x+y; 我可以在运行时使用表达式树生成等效函数,如下所示: var x = Expression.Parameter(typeof(int), "x"); var y = Expression.Parameter(typeof(int), "y"); Func<int, int, int> f = Expression.Lambd

我正试图找出如何把所有的部分放在一起,如果能从一个简单的案例开始,我希望有一个具体的源代码示例

考虑以下C#代码:

Func f=(x,y)=>x+y;
我可以在运行时使用表达式树生成等效函数,如下所示:

var x = Expression.Parameter(typeof(int), "x");
var y = Expression.Parameter(typeof(int), "y");
Func<int, int, int> f =
    Expression.Lambda<Func<int, int, int>>(
        Expression.Add(x, y),
        new[] { x, y }
    ).Compile();
var x=Expression.Parameter(typeof(int),“x”);
var y=表达式参数(typeof(int),“y”);
函数f=
Lambda(
表达式。添加(x,y),
新[]{x,y}
).Compile();
现在给出以下lambda:

Func<dynamic, dynamic, dynamic> f = (x, y) => x + y;
Func f=(x,y)=>x+y;

如何使用表达式树(大概还有
expression.Dynamic
)生成等价物?

您不能这样做,因为表达式树“可能不包含动态操作”

例如,由于+操作,并且您试图构建违反该规则的表达式树,因此无法编译以下内容:

 Expression<Func<dynamic, dynamic, dynamic>> f = (x, y) => x + y;
表达式f=(x,y)=>x+y;
如果您没有执行“添加”操作,则可以不受惩罚

有关更多信息,请参阅

编辑:

通过定义我自己的Add方法,它接受动态参数并返回动态结果,这是我能得到的最接近的结果

    class Program
{
    static void Main(string[] args)
    {

        var x = Expression.Parameter(typeof(object), "x");
        var y = Expression.Parameter(typeof(object), "y");
         Func<dynamic, dynamic, dynamic> f =
             Expression.Lambda<Func<dynamic, dynamic, dynamic>>(
                 Expression.Call(typeof(Program), "Add", null, x, y),
                 new[] { x, y }
             ).Compile();

       Console.WriteLine(f(5, 2));
       Console.ReadKey();
    }

    public static dynamic Add(dynamic x, dynamic y)
    {
        return x + y;
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var x=表达式参数(typeof(object),“x”);
var y=表达式参数(typeof(object),“y”);
函数f=
Lambda(
调用表达式(typeof(Program),“Add”,null,x,y),
新[]{x,y}
).Compile();
控制台写入线(f(5,2));
Console.ReadKey();
}
公共静态动态添加(动态x、动态y)
{
返回x+y;
}
}

非常有趣。我想这是不可能的,因为同样的原因,下面的代码没有编译:

Expression<Func<dynamic, dynamic, int>> func = (p1, p2) => p1 + p2;
Expression func=(p1,p2)=>p1+p2;
这是一个编译器错误CS1963(MS似乎没有记录):

错误CS1963:表达式树可能不包含动态操作


通过将动态C#加法表达式的CallSiteBinder传递到expression.dynamic中,可以创建表示动态C#加法表达式的表达式树。通过在原始动态表达式上运行Reflector,可以发现创建绑定器的代码。您的示例如下所示:

var x = Expression.Parameter(typeof(object), "x");
var y = Expression.Parameter(typeof(object), "y");
var binder = Binder.BinaryOperation(
    CSharpBinderFlags.None, ExpressionType.Add, typeof(Program),
    new CSharpArgumentInfo[] { 
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
Func<dynamic, dynamic, dynamic> f =
    Expression.Lambda<Func<object, object, object>>(
        Expression.Dynamic(binder, typeof(object), x, y),
        new[] { x, y }
    ).Compile();
var x=Expression.Parameter(typeof(object),“x”);
var y=表达式参数(typeof(object),“y”);
var binder=binder.BinaryOperation(
CSharpBinderFlags.None,ExpressionType.Add,typeof(程序),
新CSharpArgumentInfo[]{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)});
函数f=
Lambda(
动态表达式(活页夹、类型(对象)、x、y),
新[]{x,y}
).Compile();

我认为这是错误的。您提到的编译器错误(我知道)表明这是一个C#限制,但不一定是表达式树本身的限制。毕竟,在表达式树上下文中,C#也不允许
if
while
(或通常的语句lambdas),但您可以手动构建这样的表达式树(在.NET 4中)。但我认为这是可能的主要原因是有
Expression.Dynamic
。我很确定答案会涉及到这一点-我只是不知道如何,确切地说(而且文档很薄)。顺便说一句,在你链接到的问题中,Eric Lippert的一条评论说,“我们在编译时为那些在运行时实现动态语义的动态操作生成的codegen非常复杂;非常复杂,没有简单的方法在表达式树中清晰地表示它。“-注意突出显示的部分。所以这是可能的,只是太复杂了,以至于它因为成本太高而价值太小而被削减了。编译器限制或其他。。。现在不可能。试试吧!你必须在伊利诺伊州做。正如你所说,Eric Lippert写道,他们遗漏了该功能。同样,我不是要求使用C#“表达式树lambdas”来实现这一点,它是
x=>y
表单。我要求使用
表达式
类公开的一系列方法调用来实现这一点。后者是如何完成的并不重要——如果可以在IL中以这种方式完成,也可以在C#中完成。Eric特别提到了lambdas,而不是一般的表达式树。它看起来像是C#编译器的限制,而不是表达式树API的限制。有关详细信息,请参见回复Richard。酷,非常有趣。不确定你是否回答了OP的问题,但你肯定是按照他希望的方式解决的。太好了,这正是我想要的,谢谢!通过查看Binder.BinaryOperation()的MSDN文档,我还不太清楚另一件事——“context”参数的含义是什么?C#是否总是使用封闭类型的名称?除了将一个特定的调用站点标记为不同于所有其他调用站点(我假设是为了缓存目的)之外,它还有什么特殊的语义含义吗?比如说,如果在同一个类中有两个方法,它们可能以不同的方式分派相同的内容,那么我是否创建了两个虚拟类?@Pavel:我认为它用于确定哪些成员可以访问。例如,如果您有
私有void Foo(string s){}公共void Foo(object o){}
,则在类内的字符串上调用Foo将选择字符串重载,但在类外将选择对象重载。@Pavel:不客气!顺便说一句,接受一个答案并不会奖励赏金,所以它仍然是开放的。你还有什么要找的吗?@Quartermeister:对不起,我不太使用赏金功能,所以我错过了它是
var x = Expression.Parameter(typeof(object), "x");
var y = Expression.Parameter(typeof(object), "y");
var binder = Binder.BinaryOperation(
    CSharpBinderFlags.None, ExpressionType.Add, typeof(Program),
    new CSharpArgumentInfo[] { 
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
Func<dynamic, dynamic, dynamic> f =
    Expression.Lambda<Func<object, object, object>>(
        Expression.Dynamic(binder, typeof(object), x, y),
        new[] { x, y }
    ).Compile();