Skip to content

Statements and control flow

SyntaxDescriptionNote
if / elseBranch on a condition
switch (n) { case 1: ... }Branch on a value (statement)
n switch { 1 => 10, _ => 0 }Branch on a value (expression)Even when the cases cover every value, it can’t be omitted for int or an enum. Only union allows that.
n switch { var v when v > 0 => 10, _ => 0 }Case with a conditionIt can’t be written in the case of a switch statement.
n > 0 ? 10 : 20Conditional operator
while (n < 3)Loop while a condition holds
do { ... } while (n < 3);Loop with the check after the body
for (int i = 0; i < 3; i++)Loop a fixed number of times
for (int i = 0, j = 3; i < j; i++, j--)Loop with multiple variables
foreach (int v in a)Iterate over an arrayBecause the runtime has no enumerators.
foreach (var (a, b) in ps)Deconstruct while iterating
for { for { } }Nested loops
breakExit a loop
continueContinue to the next iteration
while (true) { ... break; }Exit an infinite loopA break in the body is needed to leave it.
if (...) { return; }Return partway through
{ … }Block
;Empty statement
const int a = 1;Local constant
int Twice(int x) { return x * 2; }Local functionOuter variables can be read but not written back (Forms that don’t compile (21)).
static int Twice(int x) { … }static local function
for (;;)Omitting parts of a for
return;Return without a value
class MyError : System.Exception { }Declaring an exception type
SyntaxDescriptionErrorReasonAlternative
goto top;Jump to a labelTUKI0001, TUKI0099by designRewrite with a loop
goto case c; / goto default;Jump to another switch caseTUKI0001by designMove the shared logic into a method and call it from both case blocks
case 0: if (c) { break; } …Exiting from the middle of a caseTUKI0001undecidedInvert the exit condition and write the rest under it
L: stmtPut a label on a statementTUKI0099by designThere’s no goto, so rewrite without a label
throw new Exception();Throw an exceptionTUKI0001, TUKI0101runtimeRepresent failure with a return value or state
try { } catch { }Catch an exceptionTUKI0001runtimeCheck the condition beforehand
throw;Rethrows a caught exceptionTUKI0001runtimeRepresent failure with a return value or state
catch (…) when (…)Catch an exception conditionallyTUKI0001runtimeSame as above
try { } finally { }Run cleanup whether or not an exception occurredTUKI0001runtimeWrite the cleanup at the end of the block
using (r) { … }Clean up when leaving a blockTUKI0001not yetCall the cleanup method yourself (with no finally, it doesn’t run when you leave early)
using var r = …;Let the declared variable’s cleanup happen automaticallyTUKI0001, TUKI0099not yetSame as above
yield return / yield breakReturn values one at a timeTUKI0001runtimePack them into an array and return it
IEnumerator<int> Nums()Returns an iteratorTUKI0001runtimeSame as above
p switch { (1, 2) => … }Deconstruct by position in a caseTUKI0001not yetWrite { Item1: 1, Item2: 2 }, or read the elements and compare them
void Bump() { n = n + 1; } (writes back to an outer variable)Modifying an outer variable from a function inside a methodTUKI0001undecidedReturn it as a value and let the caller assign it (a function that only reads the value can be written)
ref int r = ref a;Give a variable an aliasTUKI0001undecidedAssign the value and use it
lock (o) { … }Do mutual exclusionTUKI0001runtimeUnnecessary, since there is only one flow of execution
async Task Go() { await ...; }Await asynchronouslyTUKI0001, TUKI0101runtimeUse a mechanism that calls back after a delay
async void Go()Writes an async method with no return valueTUKI0001, TUKI0101runtimeSame as above
n switch { 1 => 10, 2 => 20 }Branch without a default caseTUKI0001by designAlways include _ =>. It can only be omitted for a union
foreach (var (a, b) in ps) (a type with a hand-written Deconstruct)Use your own hand-written deconstructionTUKI0001by designRead the fields by name
Declaring and using are separateBeing supported for declaration doesn’t mean it can be used. Exception types are supported as declarations, and the error appears only the moment you write the line that throws
using UnityEngine;
using Tsukimi;
public class ExpressionsIfElse : TsukimiBehaviour
{
void Start()
{
int n = 1;
if (n > 0) { n = 2; } else { n = 3; }
Debug.Log(n); // => 2
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsSwitchStatement : TsukimiBehaviour
{
void Start()
{
int n = 1;
switch (n)
{
case 1: n = 10; break;
default: n = 0; break;
}
Debug.Log(n); // => 10
}
}

A _ => case is required.

using UnityEngine;
using Tsukimi;
public class ExpressionsSwitchExpression : TsukimiBehaviour
{
void Start()
{
int n = 1;
int r = n switch { 1 => 10, _ => 0 };
Debug.Log(r); // => 10
}
}

Adding when selects that case only when the pattern matches and the condition is also true.

using UnityEngine;
using Tsukimi;
public class ExpressionsSwitchArmWhen : TsukimiBehaviour
{
void Start()
{
int n = 1;
int r = n switch { var v when v > 0 => 10, _ => 0 };
Debug.Log(r); // => 10
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsTernary : TsukimiBehaviour
{
void Start()
{
int n = 1;
int r = n > 0 ? 10 : 20;
Debug.Log(r); // => 10
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsWhile : TsukimiBehaviour
{
void Start()
{
int n = 0;
while (n < 3) { n++; }
Debug.Log(n); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsDoWhile : TsukimiBehaviour
{
void Start()
{
int n = 0;
do { n++; } while (n < 3);
Debug.Log(n); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsFor : TsukimiBehaviour
{
void Start()
{
int total = 0;
for (int i = 0; i < 3; i++) { total += i; }
Debug.Log(total); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsForMultiInit : TsukimiBehaviour
{
void Start()
{
int count = 0;
for (int i = 0, j = 3; i < j; i++, j--) { count++; }
Debug.Log(count); // => 2
}
}

foreach can only iterate over arrays.

using UnityEngine;
using Tsukimi;
public class ExpressionsForeachArray : TsukimiBehaviour
{
void Start()
{
int[] a = new int[] { 1, 2 };
int total = 0;
foreach (int v in a) { total += v; }
Debug.Log(total); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsForeachDeconstruct : TsukimiBehaviour
{
void Start()
{
(int, int)[] ps = { (1, 2) };
foreach (var (a, b) in ps) { Debug.Log(a + b); } // => 3
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsNestedLoop : TsukimiBehaviour
{
void Start()
{
int count = 0;
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++) { count++; }
}
Debug.Log(count); // => 4
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsBreak : TsukimiBehaviour
{
void Start()
{
int count = 0;
for (int i = 0; i < 5; i++)
{
if (i == 2) { break; }
count++;
}
Debug.Log(count); // => 2
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsContinue : TsukimiBehaviour
{
void Start()
{
int count = 0;
for (int i = 0; i < 5; i++)
{
if (i == 2) { continue; }
count++;
}
Debug.Log(count); // => 4
}
}

while (true) keeps looping without evaluating a condition.

using UnityEngine;
using Tsukimi;
public class ExpressionsWhileTrueBreak : TsukimiBehaviour
{
void Start()
{
int n = 0;
while (true)
{
n++;
if (n > 2) { break; }
}
Debug.Log(n); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class ExpressionsReturnEarly : TsukimiBehaviour
{
private int Level() { return 1; }
void Start()
{
Debug.Log(1); // => 1
if (Level() > 0) { return; }
Debug.Log(2);
}
}
using UnityEngine;
using Tsukimi;
public class StBlock : TsukimiBehaviour
{
void Start()
{
{
int a = 1;
Debug.Log(a); // => 1
}
}
}
using UnityEngine;
using Tsukimi;
public class StEmpty : TsukimiBehaviour
{
void Start()
{
;
Debug.Log(1); // => 1
}
}
using UnityEngine;
using Tsukimi;
public class StLocalConst : TsukimiBehaviour
{
void Start()
{
const int a = 1;
Debug.Log(a); // => 1
}
}

A function can be declared inside a method.

using UnityEngine;
using Tsukimi;
public class StLocalFunc : TsukimiBehaviour
{
void Start()
{
int Twice(int x) { return x * 2; }
Debug.Log(Twice(2)); // => 4
}
}
using UnityEngine;
using Tsukimi;
public class StLocalFuncStatic : TsukimiBehaviour
{
void Start()
{
static int Twice(int x) { return x * 2; }
Debug.Log(Twice(2)); // => 4
}
}
using UnityEngine;
using Tsukimi;
public class StForOmitted : TsukimiBehaviour
{
void Start()
{
int i = 0;
for (;;) { i++; if (i > 2) break; }
Debug.Log(i); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class StReturnVoid : TsukimiBehaviour
{
void Start()
{
Debug.Log(1); // => 1
return;
}
}

You can declare an exception type, but you can neither throw nor catch it.

using UnityEngine;
using Tsukimi;
public class MyError : System.Exception { }
public class ExCustom : TsukimiBehaviour
{
void Start()
{
Debug.Log(1); // => 1
}
}