Multi-line Lambdas
Expression Trees in C# doesn't support multi-line lambda expressions. This limitation can be avoided using CodeGenerator.Lambda construction method. The method accepts lexical scope of the lambda expression in the form of the delegate and provides access to parameters.
The following example shows how to generate lambda function which performs factorial computation using recursion:
using System;
using System.Linq.Expressions;
using static DotNext.Metaprogramming.CodeGenerator;
using static DotNext.Linq.Expressions.ExpressionBuilder;
Func<long, long> fact = Lambda<Func<long, long>>(fun =>
{
var arg = fun[0];
If((Expression)(arg.AsDynamic() > 1L))
.Then(arg.AsDynamic() * fun.Invoke(arg.AsDynamic() - 1L))
.Else(arg)
.OfType<long>()
.End();
}).Compile();
fact(3); // == 6
fun
parameter is of type LambdaContext and provide access to the function parameters. arg
is the lambda function parameter. If
starts construction of if-then-else expression. fun.Invoke
method allows to invoke lambda function recursively. OfType
describes type of conditional expression that was started by If
call. End
method call represents end of conditional expression. AsDynamic
extension method allows to convert expression node to it's dynamically-typed representation that allows to use dynamic features of C# for building expression trees.
LambdaContext
type supports decomposition so it is possible to use convenient syntax to obtain function parameters:
using System;
using static DotNext.Metaprogramming.CodeGenerator;
using static DotNext.Linq.Expressions.ExpressionBuilder;
Lambda<Func<int, int, int>>(fun =>
{
var (x, y) = fun;
Return(x.AsDynamic() + y);
});
The last expression inside of lamda function is interpreted as return value. However, explicit return is supported.
using System;
using System.Linq.Expressions;
using static DotNext.Metaprogramming.CodeGenerator;
using static DotNext.Linq.Expressions.ExpressionBuilder;
Func<long, bool> isZero = Lambda<Func<long, bool>>(fun =>
{
var arg = fun[0];
If((Expression)(arg.AsDynamic() != 0L))
.Then(() => Return(true))
.Else(() => Return(false))
.End();
}).Compile();
The equivalent code is
using System;
new Func<long, long>(arg =>
{
if(arg != 0L)
return true;
else
return false;
});
Implicit result
Lambda function builder can have optional result variable which is used to set function result without returning from it.
using System;
using static DotNext.Metaprogramming.CodeGenerator;
Lambda<Fun<long, long>>((fun, result) =>
{
Assign(result, fun[0]);
});
This feature is similar to result implicit variable in Pascal programming language. Once result assigned, it is not needed to use explicit Return method to return from the lambda function.