Loops
Metaprogramming library provides construction of for, foreach and while loops. The lexical scope constructor may have optional parameter of type LoopContext that can be used to leave outer loop from inner loop. There is no equivalent instruction in C# except unconditional control transfer using goto.
Continue()
or Break()
methods from CodeGenerator are used to pass the control to the next iteration or out of scope respectively.
foreach Loop
foreach statement may accept any expression of type implementing IEnumerable or IEnumerable<T> interface, or having public instance parameterless method GetEnumerator()
which return type has the public instance property Current
and public instance parameterless method MoveNext()
.
using System;
using static DotNext.Metaprogramming.CodeGenerator;
Lambda<Action<string>>(fun =>
{
ForEach(fun[0], ch =>
{
WriteLine(ch);
});
});
//generated code is
new Action<string>(str =>
{
foreach (char ch in str)
Console.WriteLine(ch);
});
ch
variable provides access to current element in the collection.
await foreach Loop
await foreach statement allows to enumerate over elements in asynchronous streams implementing IAsyncEnumerable<T> interface.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static DotNext.Metaprogramming.CodeGenerator;
AsyncLambda<Func<IAsyncEnumerable<char>, Task>>(fun =>
{
AwaitForEach(fun[0], ch =>
{
WriteLine(ch);
});
});
//generated code is
new Func<IAsyncEnumerable<char>, Task>(async str =>
{
await foreach (char ch in str)
Console.WriteLine(ch);
});
This type of statement is allowed within async lambda expression only.
while Loop
while loop statement is supported in two forms: while-do or do-while. Both forms are representing regular while loop existing in most popular languages such as C#, C++ and C.
using System;
using System.Linq.Expressions;
using static DotNext.Metaprogramming.CodeGenerator;
using static DotNext.Linq.Expressions.ExpressionBuilder;
Lambda<Fun<long, long>>((fun, result) =>
{
var arg = fun[0];
Assign(result, 1L); //result = 1L;
While((Expression)(arg.AsDynamic() > 1L), () =>
{
Assign(result, arg.AsDynamic()-- * fun.Result); //result *= arg--;
});
});
//generated code is
new Func<long, long>(arg =>
{
var result = 1L;
while (arg > 1L)
result *= arg--;
return result;
});
do-while loop can be constructed using DoWhile
method.
for Loop
for loop implementation in Metaprogramming library has many similarities with implementation in other C-like languages with one exception: increment (or post iteration) statement is optional, has access to the local variables declared inside of the loop and can be a compount statement. This statement is separated from main loop body with a special method call StartIteratorBlock
.
using System;
using static DotNext.Metaprogramming.CodeGenerator;
using static DotNext.Linq.Expressions.ExpressionBuilder;
Lambda<Fun<long, long>>((fun, result) =>
{
Assign(result, 1L); //result = 1L;
For(fun[0], i => i.AsDynamic() > 1L, i => PostDecrementAssign(i), i =>
{
Assign(result, i.AsDynamic() * result); //result *= i;
});
});
//generated code is
new Func<long, long>(arg =>
{
var result = 1L;
for (var i = arg; i > 1L; i--)
result *= i;
return result;
});
Plain Loop
Plain loop is similar to while(true)
loop and doesn't have built-in loop control tools. Developer need to control loop execution by calling Continue()
and Break
manually.
using System;
using static DotNext.Metaprogramming.CodeGenerator;
using static DotNext.Linq.Expressions.ExpressionBuilder;
Lambda<Fun<long, long>>((fun, result) =>
{
var arg = fun[0];
Assign(result, 1L); //result = 1L;
Loop(() =>
{
If((Expression)(arg.AsDynamic() > 1L))
.Then(() => Assign(result, arg.AsDynamic()-- * result))
.Else(Break) //break;
.End();
});
});
//generated code is
new Func<long, long>(arg =>
{
var result = 1L;
while (true)
if(arg > 1L)
result *= arg--;
else
break;
return true;
});