C# 如何将多个表达式组合成一个快速方法?

C# 如何将多个表达式组合成一个快速方法?,c#,linq,lambda,dynamicmethod,C#,Linq,Lambda,Dynamicmethod,假设我有以下表达式: Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name); Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", "); Expression<Action<T, StringBuilder>> expr3 = (t,

假设我有以下表达式:

Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name);
Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", ");
Expression<Action<T, StringBuilder>> expr3 = (t, sb) => sb.Append(t.Description);
最好的方法是什么?我希望它表现良好,理想情况下,性能相当于上述方法

更新
因此,虽然在C#3中似乎没有办法直接实现这一点,但有没有办法将表达式转换为IL,以便我可以将其与System.Reflection.Emit一起使用?

您只能在.NET 4中实现这一点。对不起,我不知道细节

编辑:

如果您对Reflection.Emit感到满意,那么可以发出一个方法,按顺序调用这些表达式

另一种选择:

创建“do”方法,即:

void Do(params Action[] actions)
{
  foreach (var a in actions) a();
}

不幸的是,在.NET3.5中,您无法构建执行一系列任意操作的表达式。以下是支持的表达式列表:

Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name);
Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", ");
Expression<Action<T, StringBuilder>> expr3 = (t, sb) => sb.Append(t.Description);
  • 算术:加、加、除、模、乘、乘、乘、反、反、幂、减、减、减、一元加
  • 创建:Bind、ElementInit、ListBind、ListInit、MemberBind、MemberInit、New、NewArrayBounds、NewArrayInit
  • 按位:And,ExclusiveOr,LeftShift()
  • 逻辑:AndAlso(&&),条件(?:),相等,大于,大于等于,小于等于,小于等于,*小于等于,NotEqual,小于等于(| |),类型为
  • 成员访问:ArrayIndex、ArrayLength、Call、Field、Property、PropertyOrField
  • 其他:Convert,ConvertChecked,Coalesce(?),Constant,Invoke,Lambda,Parameter,TypeAs,Quote
.NET 4通过添加以下表达式扩展了此API:

Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name);
Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", ");
Expression<Action<T, StringBuilder>> expr3 = (t, sb) => sb.Append(t.Description);
  • 变异:AddAssign,AddAssignChecked,AndAssign,Assign,DivideSign,ExclusiveOrAssign,LeftShiftAsign,ModuleAsign,MultilyAsign,MultilyAsignChecked,OrAssign,PostDecrementAsign,PostIncrementAsign,PowerAssign,PreDecrementAsign,PreIncrementAssign,RightShiftAsign,SubtractAssign,SubtractAssign,SubtractAssignChecked
  • 算术:递减、默认、递增、一次完成
  • 成员访问:ArrayAccess,动态
  • 逻辑:ReferenceEqual、referencentequal、TypeEqual
  • 流程:阻塞、中断、继续、空、转到、IfThen、IFTHELSE、IfFalse、IfTrue、标签、循环、返回、开关、开关箱、取消装箱、变量
  • 例外情况:接球、回撤、投掷
  • 调试:ClearDebugInfo,DebugInfo

这个表达式特别有趣。

你可以,但这不是一件小事

当您有Expression类型的变量时,可以检查其Body属性以查找表达式的数据结构

您不能要求编译器为您编译它,因为它不会得到您想要的结果。您必须解析所有表达式的主体,并以某种方式将它们组合到一个方法中,所有这些方法都是通过同时发出IL(或者,如果您觉得IL太远,则通过生成C#并对其进行编译)

正如LINQtoSQL将表达式编译为SQL查询一样,您也可以将表达式编译为所需的任何内容。你将有很多工作要做,但你只需要实现你想要支持的


在这种相当简单的情况下,我认为没有必要创建自己的LINQ提供程序。您可以使用传递的表达式,然后从那里开始。但我怀疑您的应用程序比这要复杂一些。

在4.0中,由于支持树中的块操作(尽管在C#expression编译器中不支持),这要容易得多

但是,您可以利用
StringBuilder
公开“流畅”API的事实来实现这一点;因此,您没有使用
Action
而是使用
Func
,如下所示(请注意,在本例中,表示这些表达式的实际语法是相同的):

类程序
{
静态void Main()
{
Foo(新的MyType{Name=“abc”,Description=“def”});
}
静态void Foo(T val),其中T:IMyType
{
变量表达式=新表达式[]{
(t,sb)=>sb.附加(t.名称),
(t,sb)=>sb.追加(“,”),
(t,sb)=>sb.追加(t.说明)
};
var tparam=表达式参数(类型(T),“T”);
var sbparam=表达式参数(typeof(StringBuilder),“sb”);
表达式body=sbparam;
for(int i=0;i

当然可以检查树并手动发出IL(
DynamicMethod
),但您必须做出一些决定来限制复杂性。对于所展示的代码,我可以在合理的时间内完成它(仍然不是微不足道的),但是如果您希望更复杂的
表达式
就更容易理解了

看待这个问题的另一种方法是记住,代理是多播的;您可以多次组合一个
动作

class Program
{
    static void Main()
    {
        Foo(new MyType { Name = "abc", Description = "def" });
    }

    static void Foo<T>(T val) where T : IMyType {
        var expressions = new Expression<Action<T, StringBuilder>>[] {
                (t, sb) => sb.Append(t.Name),
                (t, sb) => sb.Append(", "),
                (t, sb) => sb.Append(t.Description)
        };
        Action<T, StringBuilder> result = null;
        foreach (var expr in expressions) result += expr.Compile();
        if (result == null) result = delegate { };
        // now test it
        StringBuilder sbInst = new StringBuilder();
        result(val, sbInst);
        Console.WriteLine(sbInst.ToString());
    }
}
public class MyType : IMyType
{
    public string Name { get; set; }
    public string Description { get; set; }
}
interface IMyType
{
    string Name { get; }
    string Description { get; }

}
类程序
{
静态void Main()
{
Foo(新的MyType{Name=“abc”,Description=“def”});
}
静态void Foo(T val),其中T:IMyType{
变量表达式=新表达式[]{
(t,sb)=>sb.附加(t.名称),
(t,sb)=>sb.追加(“,”),
(t,sb)=>sb.追加(t.说明)
};
动作结果=null;
foreach(表达式中的var expr)result+=expr.Compile();
如果(result==null)result=delegate{};
//现在测试一下
StringBuilder sbInst=新的StringBuilder();
结果(val,sbInst);
安慰