Skip to content

Patterns

SyntaxDescription
switch (n) { case 1: … }Branch with a statement
case 1:Branch with a statement (numbers)
n switch { 1 => 10, _ => 0 }Branch with an expression
m switch { Mode.A => 1, _ => 0 }Branch on an enum
s switch { "a" => 1, _ => 0 }Branch on a string
s is DotChecking the type
s is Line lChecking the type and binding
case Dot:Type in a case label
s switch { Dot d => …, Line l => … }Exhaustive union cases
n is 1Matching a constant
s is nullMatching against null
s is "a"Matching a string constant
m is Mode.OnMatching an enum constant
n is < 30Relational matching
n is >= 0 and <= 100Combining conditions (and)
Next() is >= 0 and <= 100How many times the receiver is evaluated
n is 3 or 4Combining conditions (or)
s is not nullNegation
n is int and (< 0 or > 100)Precedence with parentheses
n is var xA binding that always matches
h is { V: 1 }Matching a member value
h is { V: > 0 }Matching a nested member value
k switch { < 30 => … }Relational case
k switch { 3 or 4 => … }Case with or
k switch { int n when n > 3 => … }Case condition (when)
n switch { var v when v > 0 => … }Condition after binding
SyntaxDescriptionErrorReasonInstead
p is (1, 2)Deconstruct and match by positionTUKI0001not yetMatch by member value ({ Item1: 1 })
value is int(型引数の値に対して)Apply a pattern to a type argument’s valueTUKI0001not yetRewrite it without using a type parameter
case int n when n > 3:Attach a condition to a caseTUKI0099not yetIn a switch expression, a case can carry a condition (k switch { int n when n > 3 => … })
s switch { Line l when … => … }Attach a condition to a union type patternTUKI0001not yetMove the condition into the case body, or split the case without when
n switch { 1 => 10, 2 => 20 }Omit the default case (int)TUKI0001by designAdd _ =>. It can only be omitted for a union.
m switch { Mode.A => 1, Mode.B => 2 }Omit the default case (enum)TUKI0001by designCan’t be omitted even if the cases cover every value
You can only omit the default case for a union. For int and enums, a _ => case is required even if the cases cover every value.

when can only be written on a case in a switch expression. Adding when to a case label produces TUKI0099.

using UnityEngine;
using Tsukimi;
public class PatSwitchStatement : TsukimiBehaviour
{
void Start()
{
int n = 1;
switch (n)
{
case 1: Debug.Log(10); break;
default: Debug.Log(0); break;
}
}
}
// Output
// 10
using UnityEngine;
using Tsukimi;
public class IsoSwStmtConst : TsukimiBehaviour
{
void Start()
{
int k = 1;
switch (k) { case 1: Debug.Log(1); break; default: Debug.Log(0); break; } // => 1
}
}

A switch expression evaluates to the value of the matching case. Matching an int requires a _ => case.

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

You can write enum constants as cases. Even if the cases cover every value, the _ => case can’t be omitted.

using UnityEngine;
using Tsukimi;
public enum Mode { A, B }
public class PatEnumConstant : TsukimiBehaviour
{
void Start()
{
Mode m = Mode.A;
int r = m switch { Mode.A => 1, _ => 0 };
Debug.Log(r); // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PatStringConstant : TsukimiBehaviour
{
void Start()
{
string s = "a";
int r = s switch { "a" => 1, _ => 0 };
Debug.Log(r); // => 1
}
}

Writing a type on the right of is only works for union and interface values.

using UnityEngine;
using Tsukimi;
public abstract record Shape;
public record Dot : Shape;
public record Line(int Len) : Shape;
public class IsoIsTypeUnion : TsukimiBehaviour
{
void Start()
{
Shape s = new Dot();
bool b = s is Dot;
Debug.Log(b); // => true
}
}
using UnityEngine;
using Tsukimi;
public abstract record Shape;
public record Dot : Shape;
public record Line(int Len) : Shape;
public class PtDeclaration : TsukimiBehaviour
{
void Start()
{
Shape s = new Line(3);
if (s is Line l) { Debug.Log(l.Len); } // => 3
}
}

You can write a type in a case label too. This branches on a union value by type.

using UnityEngine;
using Tsukimi;
public abstract record Shape;
public record Dot : Shape;
public record Line(int Len) : Shape;
public class PtTypeCase : TsukimiBehaviour
{
void Start()
{
Shape s = new Dot();
switch (s) { case Dot: Debug.Log(0); break; default: Debug.Log(1); break; } // => 0
}
}

Writing all the union’s cases lets you omit the _ => case.

using UnityEngine;
using Tsukimi;
public abstract record Shape;
public record Dot : Shape;
public record Line(int Len) : Shape;
public class PtExhaustiveUnion : TsukimiBehaviour
{
void Start()
{
Shape s = new Dot();
int v = s switch { Dot d => 0, Line l => l.Len };
Debug.Log(v); // => 0
}
}
using UnityEngine;
using Tsukimi;
public class IsoIsConst : TsukimiBehaviour
{
void Start()
{
int k = 1;
bool b = k is 1;
Debug.Log(b); // => true
}
}
using UnityEngine;
using Tsukimi;
public class PtConstNull : TsukimiBehaviour
{
void Start()
{
string s = null;
if (s is null) { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PtConstString : TsukimiBehaviour
{
void Start()
{
string s = "a";
if (s is "a") { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public enum Mode { Off, On }
public class PtConstEnum : TsukimiBehaviour
{
void Start()
{
Mode m = Mode.On;
if (m is Mode.On) { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PtRelational : TsukimiBehaviour
{
void Start()
{
int k = 1;
if (k is < 30) { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PtAnd : TsukimiBehaviour
{
void Start()
{
int k = 1;
if (k is >= 0 and <= 100) { Debug.Log(1); } // => 1
}
}

The left side of is is evaluated only once. Even with two conditions, Next() is called once.

using UnityEngine;
using Tsukimi;
public class PtAndOnce : TsukimiBehaviour
{
int Next()
{
Debug.Log(9); // => 9
return 1;
}
void Start()
{
if (Next() is >= 0 and <= 100) { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PtOr : TsukimiBehaviour
{
void Start()
{
int k = 3;
if (k is 3 or 4) { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PtNot : TsukimiBehaviour
{
void Start()
{
string s = "a";
if (s is not null) { Debug.Log(1); } // => 1
}
}
using UnityEngine;
using Tsukimi;
public class PtParen : TsukimiBehaviour
{
void Start()
{
int k = 1;
if (k is int and (< 0 or > 100)) { Debug.Log(1); }
}
}
using UnityEngine;
using Tsukimi;
public class PtVar : TsukimiBehaviour
{
void Start()
{
int k = 1;
if (k is var x) { Debug.Log(x); } // => 1
}
}

Inside { }, write the member name and the value to match. Only a struct or record you declared yourself can go on the left.

using UnityEngine;
using Tsukimi;
public struct Holder { public int V { get; set; } }
public class PtProperty : TsukimiBehaviour
{
void Start()
{
Holder h = new Holder();
if (h is { V: 1 }) { Debug.Log(1); }
}
}
using UnityEngine;
using Tsukimi;
public struct Holder { public int V { get; set; } }
public class PtNested : TsukimiBehaviour
{
void Start()
{
Holder h = new Holder();
if (h is { V: > 0 }) { Debug.Log(1); }
}
}
using UnityEngine;
using Tsukimi;
public class IsoSwExprRel : TsukimiBehaviour
{
void Start()
{
int k = 1;
int v = k switch { < 30 => 1, _ => 0 };
Debug.Log(v); // => 1
}
}
using UnityEngine;
using Tsukimi;
public class IsoSwExprOr : TsukimiBehaviour
{
void Start()
{
int k = 3;
int v = k switch { 3 or 4 => 1, _ => 0 };
Debug.Log(v); // => 1
}
}
using UnityEngine;
using Tsukimi;
public class IsoSwExprWhen : TsukimiBehaviour
{
void Start()
{
int k = 5;
int v = k switch { int n when n > 3 => n, _ => 0 };
Debug.Log(v); // => 5
}
}
using UnityEngine;
using Tsukimi;
public class R_wc : TsukimiBehaviour
{
void Start()
{
int n = 1;
int r = n switch { var v when v > 0 => 10, _ => 0 };
Debug.Log(r); // => 10
}
}