Skip to content

struct and record

SyntaxDescription
struct Point { public int X; }Field
public Point(int x)Constructor
public int Twice()Methods
public int X { get; set; }Properties
public int X { get; init; }Properties with an init accessor
public int Twice { get { … } }Computed property
public static int Zero()Static method
public static readonly int Zero = 0;Static readonly field
public static readonly Point Origin = new Point();Static readonly field with a struct value
struct Outer { public Inner I; }Nested structs
readonly struct Pointreadonly struct
ref struct Spanref struct
private Point held;Holding as a field
public Point held;Assigning via a public field
Point[] ps = new Point[2];Array element
void Use(Point p)Receiving as a parameter
Point Make()Returning as a return value
default(Point)Generating a default value
public static Point operator +(Point a, Point b)Defining a binary operator
operator +(M x, M y)Defining addition
operator == / operator !=Defining equality
operator < / operator >Defining comparison
operator !(M x)Defining negation
operator ++(M x)Defining an increment operator
operator <<(M x, int n)Defining a shift operator
operator true / operator falseDefining the true / false operators
public class Holder { … }Declaring your own reference type
record Tag(int N, string S);Declaring with positional parameters
t with { N = 2 }with expression
a == bComparison as a value
(int n, int m) = t;Deconstruction
public int Twice()Methods on a record
record Leaf(int N) : Base;Inheriting a base record
record Leaf(int N, int M) : Base(N);Inheriting a base with positional parameters
SyntaxDescriptionErrorReasonInstead
public void Bump() { X++; }A method that mutates thisTUKI0001not yetReturn a new value instead
public static Point Current = new Point();A mutable static fieldTUKI0001by designMake it static readonly (put values that change in a Behaviour field)
record Leaf(int A, int M) : Base(A);Pass a differently named parameter to the baseTUKI0001not yetMatch the base parameter’s name (Base(N))
public int Twice { get { … } set { … } }A mutable computed propertyTUKI0001not yetMake it get-only, or use a method (it’s copied as a value, so you can’t write it back)
new Holder()(class に対して)Instantiate a reference type you defined yourselfTUKI0001runtimeMake it a struct / record (treated as a value)
h.V = 1;(class の値に対して)Write to a field of a reference type you defined yourselfTUKI0001runtimeSame as above
Holder.Make()(class の静的メソッド)Call a static method of a reference type you defined yourselfTUKI0001runtimeMake it a Behaviour method, or use a struct
When inheriting a base with positional parameters, you can only pass the base a parameter with a matching name. record Leaf(int N, int M) : Base(N); compiles, but record Leaf(int A, int M) : Base(A); does not. A derived value lays the base’s fields and its own fields out flat, and construction assigns each one to its place by name.
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public int Y;
}
public class SrDeclareFields : TsukimiBehaviour
{
void Start()
{
Point p = new Point();
p.X = 1;
Debug.Log(p.X + p.Y); // => 1
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public Point(int x) { X = x; }
}
public class SrConstructor : TsukimiBehaviour
{
void Start()
{
Point p = new Point(3);
Debug.Log(p.X); // => 3
}
}

You can write methods. You can’t write a method that mutates this (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public int Twice() { return X * 2; }
}
public class SrMethod : TsukimiBehaviour
{
void Start()
{
Point p = new Point();
p.X = 2;
Debug.Log(p.Twice()); // => 4
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X { get; set; }
}
public class SrProperty : TsukimiBehaviour
{
void Start()
{
Point p = new Point();
p.X = 1;
Debug.Log(p.X); // => 1
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X { get; init; }
}
public class SrInitAccessor : TsukimiBehaviour
{
void Start()
{
Point p = new Point { X = 1 };
Debug.Log(p.X); // => 1
}
}

You can write get only. You can’t write set (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public int Twice { get { return X * 2; } }
}
public class R_cps : TsukimiBehaviour
{
void Start()
{
Point p = new Point();
p.X = 2;
Debug.Log(p.Twice); // => 4
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public static int Zero() { return 0; }
}
public class SrStaticMethod : TsukimiBehaviour
{
void Start()
{
Debug.Log(Point.Zero()); // => 0
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public static readonly int Zero = 0;
}
public class SrStaticReadonlyPrimitive : TsukimiBehaviour
{
void Start()
{
Debug.Log(Point.Zero); // => 0
}
}

It can also hold a value of a struct or record you declared yourself. You can’t write a mutable static field (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public static readonly Point Origin = new Point();
}
public class R_srsv : TsukimiBehaviour
{
void Start()
{
Point p = Point.Origin;
Debug.Log(p.X); // => 0
}
}

A struct field can hold another struct.

using UnityEngine;
using Tsukimi;
public struct Inner
{
public int V;
}
public struct Outer
{
public Inner I;
}
public class SrNested : TsukimiBehaviour
{
void Start()
{
Outer o = new Outer();
o.I.V = 1;
Debug.Log(o.I.V); // => 1
}
}
using UnityEngine;
using Tsukimi;
public readonly struct Point
{
public readonly int X;
public Point(int x) { X = x; }
}
public class SrReadonlyStruct : TsukimiBehaviour
{
void Start()
{
Point p = new Point(1);
Debug.Log(p.X); // => 1
}
}

A type declared as ref struct can also be created and used as a value.

using UnityEngine;
using Tsukimi;
public ref struct Span
{
public int X;
}
public class SrRefStruct : TsukimiBehaviour
{
void Start()
{
Span s = new Span();
s.X = 1;
Debug.Log(s.X); // => 1
}
}

You can hold it as a Behaviour field.

using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
}
public class SrField : TsukimiBehaviour
{
private Point held;
void Start()
{
held.X = 1;
Debug.Log(held.X); // => 1
}
}

Make it public and you can set the value from the inspector.

using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
}
public class SrPublicField : TsukimiBehaviour
{
public Point held;
void Start()
{
Debug.Log(held.X); // => 0
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
}
public class SrArray : TsukimiBehaviour
{
void Start()
{
Point[] ps = new Point[2];
ps[0].X = 1;
Debug.Log(ps[0].X); // => 1
}
}

A copy is passed as the argument, so changes made by the callee don’t affect the caller’s value.

using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
}
public class SrParameter : TsukimiBehaviour
{
void Start()
{
Use(new Point());
}
private void Use(Point p)
{
Debug.Log(p.X); // => 0
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
}
public class SrReturnValue : TsukimiBehaviour
{
void Start()
{
Point p = Make();
Debug.Log(p.X); // => 0
}
private Point Make()
{
return new Point();
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
}
public class SrDefaultValue : TsukimiBehaviour
{
void Start()
{
Point p = default(Point);
Debug.Log(p.X); // => 0
}
}
using UnityEngine;
using Tsukimi;
public struct Point
{
public int X;
public static Point operator +(Point a, Point b)
{
Point r = new Point();
r.X = a.X + b.X;
return r;
}
}
public class SrOperator : TsukimiBehaviour
{
void Start()
{
Point a = new Point();
Point b = new Point();
Point c = a + b;
Debug.Log(c.X); // => 0
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static M operator +(M x, M y) { return new M(x.V + y.V); }
}
public class OpBinAdd : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
M b = a + a;
Debug.Log(b.V); // => 2
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static bool operator ==(M x, M y) { return x.V == y.V; }
public static bool operator !=(M x, M y) { return x.V != y.V; }
public override bool Equals(object o) { return false; }
public override int GetHashCode() { return 0; }
}
public class OpEqPair : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
Debug.Log(a == a); // => true
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static bool operator <(M x, M y) { return x.V < y.V; }
public static bool operator >(M x, M y) { return x.V > y.V; }
}
public class OpCmpPair : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
Debug.Log(a < a); // => false
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static M operator !(M x) { return x; }
}
public class OpNot : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
M b = !a;
Debug.Log(b.V); // => 1
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static M operator ++(M x) { return new M(x.V + 1); }
}
public class OpInc : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
a++;
Debug.Log(a.V); // => 2
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static M operator <<(M x, int n) { return x; }
}
public class OpShift : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
M b = a << 1;
Debug.Log(b.V); // => 1
}
}
using UnityEngine;
using Tsukimi;
public struct M {
public float V;
public M(float v) { V = v; }
public static bool operator true(M x) { return x.V > 0; }
public static bool operator false(M x) { return x.V <= 0; }
}
public class OpTrueFalse : TsukimiBehaviour
{
void Start()
{
M a = new M(1);
if (a) { Debug.Log(1); } // => 1
}
}

The declaration itself compiles, but using it is rejected (Forms that don’t compile). struct and record work.

using UnityEngine;
using Tsukimi;
public class Holder { public int V; }
public class C2Decl : TsukimiBehaviour
{
void Start()
{
Debug.Log(1); // => 1
}
}
using UnityEngine;
using Tsukimi;
public record Tag(int N, string S);
public class SrRecordPositional : TsukimiBehaviour
{
void Start()
{
Tag t = new Tag(1, "a");
Debug.Log(t.N); // => 1
}
}
using UnityEngine;
using Tsukimi;
public record Tag(int N);
public class SrRecordWith : TsukimiBehaviour
{
void Start()
{
Tag t = new Tag(1);
Tag u = t with { N = 2 };
Debug.Log(u.N); // => 2
}
}
using UnityEngine;
using Tsukimi;
public record Tag(int N);
public class SrRecordEquality : TsukimiBehaviour
{
void Start()
{
Tag a = new Tag(1);
Tag b = new Tag(1);
Debug.Log(a == b); // => true
}
}
using UnityEngine;
using Tsukimi;
public record Tag(int N, int M);
public class SrRecordDeconstruct : TsukimiBehaviour
{
void Start()
{
Tag t = new Tag(1, 2);
(int n, int m) = t;
Debug.Log(n + m); // => 3
}
}
using UnityEngine;
using Tsukimi;
public record Tag(int N)
{
public int Twice() { return N * 2; }
}
public class SrRecordMethod : TsukimiBehaviour
{
void Start()
{
Tag t = new Tag(2);
Debug.Log(t.Twice()); // => 4
}
}
using UnityEngine;
using Tsukimi;
public abstract record Base;
public record Leaf(int N, int M) : Base;
public class SrRecordInherit : TsukimiBehaviour
{
void Start()
{
Leaf l = new Leaf(1, 2);
Debug.Log(l.N + l.M); // => 3
}
}

Inheriting a base with positional parameters

Section titled “Inheriting a base with positional parameters”

You can also inherit a base that has positional parameters. You can only pass the base a parameter with the same name (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public abstract record Base(int N);
public record Leaf(int N, int M) : Base(N);
public class R_recpos : TsukimiBehaviour
{
void Start()
{
Leaf l = new Leaf(1, 2);
Debug.Log(l.M); // => 2
}
}