How to combine multiple expressions into a quick method?

Suppose I have the following expressions:

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); 

I would like to compile them into a method / delegate equivalent to the following:

 void Method(T t, StringBuilder sb) { sb.Append(t.Name); sb.Append(", "); sb.Append(t.Description); } 

What is the best way to approach this? I would like it to work well, ideally with a performance equivalent to the method described above.

UPDATE So, although there seems to be no way to do this directly in C # 3, is there a way to convert the expression to IL so that I can use it with System.Reflection.Emit?

+6
c # lambda linq dynamicmethod
source share
5 answers

Unfortunately, in .NET 3.5 you cannot create an expression that performs a series of arbitrary operations. Here is a list of supported expressions:

  • Arithmetic: Add, AddChecked, Divide, Modulo, Multiply, MultiplyChecked, Negate, NegateChecked, Power, Subtract, SubtractChecked, UnaryPlus
  • Creation: Bind, ElementInit, ListBind, ListInit, MemberBind, MemberInit, New, NewArrayBounds, NewArrayInit
  • Bitwise: And, ExclusiveOr, LeftShift (<), Not, or, RightShift (β†’)
  • Logical: AndAlso (& &), Condition (? :), Equal, GreaterThan, GreaterThanOrEqual, LessThan, * LessThanOrEqual, NotEqual, OrElse (||), TypeIs
  • Member Access: ArrayIndex, ArrayLength, Call, Field, Property, PropertyOrField
  • Other: Convert, Convert, Coalesce (??), Constant, Invoke, Lambda, Parameter, TypeAs, Quote

.NET 4 extends this API by adding the following expressions:

  • Mutation: AddAssign, AddAssignChecked, AndAssign, Assign, DivideAssign, ExclusiveOrAssign, LeftShiftAssign, ModuloAssign, MultiplyAssign, MultiplyAssignChecked, OrAssign, PostDecrementAssign, SubIncrementAssign, PowerAssignign, PostIncrementAssign, PowerAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign PreignAssignign
  • Arithmetic: Decrement, Default, Increment, OnesComplement
  • Member Access: ArrayAccess, dynamic
  • Boolean: ReferenceEqual, ReferenceNotEqual, TypeEqual
  • Flow: Lock, Break, Continue, Empty, Goto, IfThen, IfThenElse, IfFalse, IfTrue, Label, Loop, Return, Switch, SwitchCase, Unbox, Variable
  • Exceptions: Catch, Rethrow, Throw
  • Debugging: ClearDebugInfo, DebugInfo

Of particular interest is the interesting Block expression.

+4
source share

You can, but this is not a trivial task.

If you have a variable like Expression, you can check its Body property to find the data structure of the expression.

You cannot ask the compiler to compile it for you, because it will not get the desired result. You will need to parse the bodies of all your expressions and somehow combine them into one method, all by emitting IL at the same time (or by creating C # and compiling if you feel that IL is a step too far )

Just as LINQ-to-SQL compiles an expression into an SQL query, so you can compile your expressions into everything you need. You have a lot of work ahead, but you only need to realize what you want to support.

In this rather trivial case, I do not consider it necessary to create my own LINQ provider. You could just work with the expression that was passed in and go from there. But I suspect your application is a little more complicated than that.

+1
source share

In 4.0, this is much simpler thanks to the support of block operations in the tree (although not in the C # expression compiler).

However, you can do this using the fact that StringBuilder provides a "free" API; so instead of Action<T,StringBuilder> you have Func<T,StringBuilder,StringBuilder> - as shown below (note that the actual syntax of the expression of these expressions is identical in this case):

 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<Func<T, StringBuilder, StringBuilder>>[] { (t, sb) => sb.Append(t.Name), (t, sb) => sb.Append(", "), (t, sb) => sb.Append(t.Description) }; var tparam = Expression.Parameter(typeof(T), "t"); var sbparam = Expression.Parameter(typeof(StringBuilder), "sb"); Expression body = sbparam; for (int i = 0; i < expressions.Length; i++) { body = Expression.Invoke(expressions[i], tparam, body); } var func = Expression.Lambda<Func<T, StringBuilder, StringBuilder>>( body, tparam, sbparam).Compile(); // now test it StringBuilder sbInst = new StringBuilder(); func(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; } } 

Of course, you can check the trees and emit IL manually (perhaps DynamicMethod ), but you will need to make some decisions about limiting complexity. For the code presented, I could do it in a reasonable amount of time (still not trivial), but if you expect something more complicated Expression more you are fried.

+1
source share

You can only do this in .NET 4. Sorry, I don’t know the details.

Edit:

If you like Reflection.Emit, you can fix the method that calls these expressions in sequence.

Another alternative:

Create a do method, i.e.

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

Another way to look at this problem is to remember that delegates are multi-sheeted; you can combine Action many times;

 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; } } 
0
source share

All Articles