On this page

if, select and loops

Use a condition to choose, a loop to repeat, and a named transfer to make the destination clear. These snippets belong inside application actions; their inputs and helper actions come from the surrounding application.

if: choose a branch or return early

if accountIsBlocked then
    return "Blocked"
else if amount > 5000 then
    return "Needs review"
else
    return "Ready"
end if

Conditions are Boolean. Only the chosen branch runs; return completes the enclosing action.

if(condition, yes, no): choose a value

let label = if(needsReview, "Waiting for review", "Ready")

Only the selected result is evaluated. The two-argument form if(condition, value) returns nothing when unselected; it is not null coalescing.

select / case: choose the first matching case

select sourceName
    case "customers", "contacts"
        ImportContacts()
    case "orders"
        ImportOrders()
    else
        throw new Global.System.ArgumentException("Unknown import", "sourceName")
end select

The subject is evaluated once. Cases are considered in order, with no fall-through. Ordinary String matching is case-insensitive.

select / take: produce a value from a case

let fee as Decimal = select amount
    case <= 100
        take 2
    case <= 1000
        take 5
    else
        take 10
end select

Take supplies the selected value; return would exit the action. Every normally completing selected path needs a result.

for_each: visit items in a sequence

for_each address in addresses
    if address is nothing then
        continue
    end if
    let label = address.Trim()
    if label is empty then
        continue
    end if
    SendWelcome(label)
end for_each

Each item is visited in source order. An empty source runs no iterations. For an IAsyncEnumerable source, ordinary for_each selects asynchronous enumeration without a separate await for_each spelling.

break / continue / exit: control the current loop

for_each item in items
    if item.IsInvalid then
        continue
    end if
    if item.IsCritical then
        break
    end if
    Process(item)
end for_each

Continue proceeds to the next iteration; break ends the innermost loop. Bare exit means the same as break. In sequential nesting, exit while, exit repeat and exit for leave the nearest loop of that kind; exit for includes for_each. Required cleanup still runs.

for_each / range / step: iterate numeric bounds

for_each i in range 10 to 0 step -2
    ProcessNumber(i)
end for_each

To is inclusive: this visits 10, 8, 6, 4, 2 and 0. Without step, direction follows the endpoints. The iteration variable is readonly. Numeric iteration uses for_each in range rather than a separate for i = header.

where / order_by / skip / take: shape a loop's input

for_each customer in customers
    where customer.IsActive
    order_by customer.Name
    skip 20
    take 10

    ProcessCustomer(customer)
end for_each

The clause order is filter, order, skip, take. These clauses shape the sequence before body entry; they do not mean select's result-producing take.

while: test before each iteration

while remaining > 0
    ProcessBatch()
    remaining -= batchSize
end while

A false initial condition runs no iterations. The surrounding action must ensure a positive batchSize so this loop makes progress.

repeat / until: run before testing

repeat
    ProcessBatch()
    remaining -= batchSize
until remaining <= 0

The body runs at least once. Until stops when true; a trailing while continues while true. The condition closes the loop, with no following end repeat. Continue still reaches that condition.

repeat times / forever: repeat explicitly

repeat 3 times
    RefreshCache()
end repeat

repeat forever
    ProcessNextRequest()
end repeat

Counted and forever forms close with end repeat. A nonpositive count runs no iterations. Use break or a named loop transfer when the body should stop an unbounded loop.

with name / break / continue: target an enclosing loop

for_each customer in customers with name: customers_loop
    for_each order in customer.Orders
        if order.IsInvalid then
            continue customers_loop
        end if
        if order.IsCritical then
            break customers_loop
        end if
        ProcessOrder(order)
    end for_each
end for_each

The named continue moves to the next customer; the named break ends customer processing. Names are case-insensitive. The clause also applies to the other approved loop forms. It does not widen concurrent iteration or callable boundaries.

CurrentLoop / LastLoop: inspect loop progress

for_each order in orders
    ProcessOrder(order)
end for_each
Global.System.Console.WriteLine("Processed {{LastLoop.NumberOfIterations}} orders")

CurrentLoop describes the innermost executing loop. After a loop completes, LastLoop describes that loop within its containing lexical block. LastLoop.TerminatedByExit distinguishes explicit termination from normal exhaustion or condition completion. See the metadata rules for available fields and lifetimes.

mark / goto: name and jump to a destination

let remaining = 3
mark next_attempt
if TrySend() then
    goto sent
end if
remaining -= 1
if remaining > 0 then
    goto next_attempt
end if
return false

mark sent
return true

Goto supports forward and backward jumps to marks. It is ordinary control flow, not structured retry. A mark is not a loop name; jumps cannot bypass initialization, cleanup or concurrency obligations. Read the destination rules.

For full selection, scope and concurrent-loop behavior, use the control-flow specification.

Previous: Values and operators · Next: Actions and callables