Skip to content

Members

SyntaxDescription
private int count;Field
private int count = 5;Declaration with initializer
private const int Max = 10;Constant
private static int Count;Static field
private static readonly int Max = 10;Static readonly field
private void Do()Method declaration
private int Get()Method with a return value
private int Add(int a, int b)Method with parameters
int Add(int a, int b = 2)Default argument
Add(b: 2, a: 1)Named argument
Add(int, int) / Add(float, float)Overload
private int Twice(int n) => n * 2;Expression-bodied method
private static int Zero()Static method
public int Value { get; set; }Auto-property
public int Doubled { get { return n * 2; } }Computed property
{ get; private set; }Different accessibility for get and set
{ get; set; } = 1;Property initializer
{ get; }Getter-only auto-property
{ get; init; }init accessor
[field: SerializeField]Attribute on the backing field
public virtual int V { get { … } }Virtual property
public int this[int i]Indexer
this[0] = 3;Writing to an indexer
public event System.Action Fired;event declaration
event … { add { } remove { } }Writing subscribe/unsubscribe accessors
~C() { }Finalizer
private volatile int V;volatile
public readonly int Read()readonly member of a value type
Add(1) / Add(1, 2)Selection by argument count
Show(1) / Show("a")Selection by argument type
Show(int) が Show(long) に勝つPreference for the candidate with fewer conversions
Sum(1, 2)Expansion into a variable-length argument
Sum(int,int) が params に勝つPreference for the normal candidate
Add(1)Omitting a default argument
Pick(1, 2)Inference from arguments
First(new int[] { 1 })Inference from array elements
Left(1, "a")Inference of two type parameters
Depth(new int[1][])Inference from a nested array
true ? 1 : 2LCommon type from both sides of ?:
public struct Inner { ... }Nested type
public partial class CType written in parts
abstract int Get(); / overrideImplementing an abstract method
virtual int Get() / overrideOverriding a default implementation
SyntaxDescriptionErrorReasonInstead
public class Base : TsukimiBehaviour ... public class Leaf : BaseInherit from a concrete typeTUKI0109undecidedMake the base abstract
Fired?.Invoke()Notify with eventTUKI0108runtimeCall the method directly, or send it by specifying a name
Fired += HandlerSubscribe to eventTUKI0108, TUKI0099runtimeSame as above
public static extern int Native()Declare an extern implementationTUKI0001runtimeCall a method on the runtime
Read(int v) と Read(in int v) を並べるDistinguish candidates by how the argument is passedTUKI0001undecidedRename it. in alone can be used (Variables and argument passing)
public int Twice { get { … } set { … } }(struct の中)Writable computed property (value type)TUKI0001not yetUse get only, or make it a method (struct and record)
public int Extra { get; set; }(record の中)Property (record)TUKI0001by designUse a positional parameter, or make it a method
c[0] = 1;(struct の中の添字)Writing to an indexer (value type)TUKI0001not yetMake it a method that returns a new value, or use an indexer on the behaviour
panel[0](別のふるまいの添字)Read an indexer on another behaviourTUKI0001undecidedPut a method on the other side and call it
For event, only the declaration compiles. A line written as Fired += Handler and a line written as Fired?.Invoke() both produce TUKI0108.

~C() and volatile compile and run. No error or warning appears, but writing them does not change behaviour. Members that initialize the type itself (static constructors, module initializers) do not run either (Initialization and execution order).

Whether a property compiles depends on where it’s placed and the shape of its accessors.

Where it’s placedAuto-propertyComputed (get only)Computed (with set)
BehaviourCompilesCompilesCompiles
structCompilesCompilesDoesn’t compile
recordDoesn’t compileDoesn’t compileDoesn’t compile
InterfaceCompiles (the implementing side must also use an auto-property)Doesn’t compile
using UnityEngine;
using Tsukimi;
public class MemField : TsukimiBehaviour
{
private int count;
void Start()
{
Debug.Log(count); // => 0
}
}
using UnityEngine;
using Tsukimi;
public class MemFieldInitializer : TsukimiBehaviour
{
private int count = 5;
void Start()
{
Debug.Log(count); // => 5
}
}
using UnityEngine;
using Tsukimi;
public class MemConst : TsukimiBehaviour
{
private const int Max = 10;
void Start()
{
Debug.Log(Max); // => 10
}
}

Reads and writes without creating an instance. The value is separate per behaviour instance and is not shared between instances.

using UnityEngine;
using Tsukimi;
public class MemStaticField : TsukimiBehaviour
{
private static int Count;
void Start()
{
Count = 1;
Debug.Log(Count); // => 1
}
}

static readonly compiles, but it doesn’t for a struct type (struct and record).

using UnityEngine;
using Tsukimi;
public class MemStaticReadonly : TsukimiBehaviour
{
private static readonly int Max = 10;
void Start()
{
Debug.Log(Max); // => 10
}
}
using UnityEngine;
using Tsukimi;
public class MemMethod : TsukimiBehaviour
{
private void Do() { Debug.Log(1); } // => 1
void Start()
{
Do();
}
}
using UnityEngine;
using Tsukimi;
public class MemMethodReturn : TsukimiBehaviour
{
private int Get() { return 1; }
void Start()
{
Debug.Log(Get()); // => 1
}
}
using UnityEngine;
using Tsukimi;
public class MemMethodParameters : TsukimiBehaviour
{
private int Add(int a, int b) { return a + b; }
void Start()
{
Debug.Log(Add(1, 2)); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class MemDefaultArgument : TsukimiBehaviour
{
private int Add(int a, int b = 2) { return a + b; }
void Start()
{
Debug.Log(Add(1)); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class MemNamedArgument : TsukimiBehaviour
{
private int Add(int a, int b) { return a + b; }
void Start()
{
Debug.Log(Add(b: 2, a: 1)); // => 3
}
}
using UnityEngine;
using Tsukimi;
public class MemOverload : TsukimiBehaviour
{
private int Add(int a, int b) { return a + b; }
private float Add(float a, float b) { return a + b; }
void Start()
{
Debug.Log(Add(1, 2) + Add(1f, 2f)); // => 6
}
}
using UnityEngine;
using Tsukimi;
public class MemExpressionBodied : TsukimiBehaviour
{
private int Twice(int n) => n * 2;
void Start()
{
Debug.Log(Twice(2)); // => 4
}
}
using UnityEngine;
using Tsukimi;
public class MemStaticMethod : TsukimiBehaviour
{
private static int Zero() { return 0; }
void Start()
{
Debug.Log(Zero()); // => 0
}
}
using UnityEngine;
using Tsukimi;
public class MemAutoProperty : TsukimiBehaviour
{
public int Value { get; set; }
void Start()
{
Value = 1;
Debug.Log(Value); // => 1
}
}

A property whose get body you write yourself. Inside a struct, you cannot write set (struct and record).

using UnityEngine;
using Tsukimi;
public class MemComputedProperty : TsukimiBehaviour
{
private int n;
public int Doubled { get { return n * 2; } }
void Start()
{
n = 3;
Debug.Log(Doubled); // => 6
}
}
using UnityEngine;
using Tsukimi;
public class MbAccessorAcc : TsukimiBehaviour
{
void Start()
{
V = 1;
Debug.Log(V); // => 1
}
public int V { get; private set; }
}
using UnityEngine;
using Tsukimi;
public class MbAutoPropInit : TsukimiBehaviour
{
void Start()
{
Debug.Log(V); // => 1
}
public int V { get; set; } = 1;
}
using UnityEngine;
using Tsukimi;
public class MbGetterOnly : TsukimiBehaviour
{
void Start()
{
Debug.Log(V); // => 0
}
public int V { get; }
}
using UnityEngine;
using Tsukimi;
public class MbInitAccessor : TsukimiBehaviour
{
void Start()
{
Debug.Log(V); // => 0
}
public int V { get; init; }
}
using UnityEngine;
using Tsukimi;
public class MbFieldAttr : TsukimiBehaviour
{
void Start()
{
Debug.Log(V); // => 0
}
[field: SerializeField]
public int V { get; set; }
}

A property marked virtual. To override it in a derived type, make the base type abstract.

using UnityEngine;
using Tsukimi;
public class MbVirtualProp : TsukimiBehaviour
{
void Start()
{
Debug.Log(V); // => 1
}
public virtual int V { get { return 1; } }
}

Declaring this[int i] lets you read a value of that type as v[i]. The value written in the brackets arrives as the accessor’s argument.

using UnityEngine;
using Tsukimi;
public class MbIndexer : TsukimiBehaviour
{
void Start()
{
Debug.Log(this[2]); // => 20
}
public int this[int i] { get { return i * 10; } }
}

Writing set too lets you write with v[i] = x. The value being written arrives in value, and the index arrives as an argument.

using UnityEngine;
using Tsukimi;
public class MbIndexerSet : TsukimiBehaviour
{
private int a;
private int b;
void Start()
{
this[0] = 3;
this[1] = 4;
Debug.Log(this[0] * 10 + this[1]); // => 34
}
public int this[int i]
{
get { return i == 0 ? a : b; }
set { if (i == 0) { a = value; } else { b = value; } }
}
}

The event declaration compiles. Writing a subscribe or fire line produces TUKI0108.

using UnityEngine;
using Tsukimi;
public class MbEventField : TsukimiBehaviour
{
void Start()
{
Debug.Log(1); // => 1
}
public event System.Action Fired;
}

Writing add and remove yourself also compiles, but only as a declaration.

using UnityEngine;
using Tsukimi;
public class MbEventAcc : TsukimiBehaviour
{
void Start()
{
Debug.Log(1); // => 1
}
public event System.Action Fired { add { } remove { } }
}

The runtime has no mechanism to call finalizers, so the body never runs.

using UnityEngine;
using Tsukimi;
public class MbFinalizer : TsukimiBehaviour
{
void Start()
{
Debug.Log(1); // => 1
}
~MbFinalizer() { }
}

There is no concurrently running thread of execution, so adding volatile does not change the meaning of reads and writes.

using UnityEngine;
using Tsukimi;
public class MbVolatile : TsukimiBehaviour
{
void Start()
{
Debug.Log(V); // => 0
}
private volatile int V;
}

Can be attached to a member that does not modify the value. Value types are copied each time they’re passed, so adding it does not change runtime behaviour.

using UnityEngine;
using Tsukimi;
public struct Point { public int X;
public readonly int Read() { return X; } }
public class MbReadonlyMember : TsukimiBehaviour
{
void Start()
{
Point p = new Point();
Debug.Log(p.Read()); // => 0
}
}
using UnityEngine;
using Tsukimi;
public class OvByArity : TsukimiBehaviour
{
void Start()
{
Debug.Log(Add(1)); // => 1
Debug.Log(Add(1, 2)); // => 3
}
private int Add(int a) { return a; }
private int Add(int a, int b) { return a + b; }
}
using UnityEngine;
using Tsukimi;
public class OvByType : TsukimiBehaviour
{
void Start()
{
Debug.Log(Show(1)); // => 1
Debug.Log(Show("a")); // => 2
}
private int Show(int a) { return 1; }
private int Show(string a) { return 2; }
}

Preference for the candidate with fewer conversions

Section titled “Preference for the candidate with fewer conversions”
using UnityEngine;
using Tsukimi;
public class OvBetterConv : TsukimiBehaviour
{
void Start()
{
Debug.Log(Show(1)); // => 2
}
private int Show(long a) { return 1; }
private int Show(int a) { return 2; }
}
using UnityEngine;
using Tsukimi;
public class OvExpanded : TsukimiBehaviour
{
void Start()
{
Debug.Log(Sum(1, 2)); // => 2
Debug.Log(Sum(new int[] { 1, 2 })); // => 2
}
private int Sum(params int[] xs) { return xs.Length; }
}
using UnityEngine;
using Tsukimi;
public class OvNormalBeatsParams : TsukimiBehaviour
{
void Start()
{
Debug.Log(Sum(1, 2)); // => 1
}
private int Sum(int a, int b) { return 1; }
private int Sum(params int[] xs) { return 2; }
}
using UnityEngine;
using Tsukimi;
public class OvDefaultArg : TsukimiBehaviour
{
void Start()
{
Debug.Log(Add(1)); // => 3
Debug.Log(Add(1, 5)); // => 6
}
private int Add(int a, int b = 2) { return a + b; }
}
using UnityEngine;
using Tsukimi;
public class OvInferArgs : TsukimiBehaviour
{
void Start()
{
Debug.Log(Pick(1, 2)); // => 1
}
private T Pick<T>(T a, T b) { return a; }
}
using UnityEngine;
using Tsukimi;
public class OvInferLower : TsukimiBehaviour
{
void Start()
{
Debug.Log(First(new int[] { 1 })); // => 1
}
private T First<T>(T[] xs) { return xs[0]; }
}
using UnityEngine;
using Tsukimi;
public class OvInferTwo : TsukimiBehaviour
{
void Start()
{
Debug.Log(Left(1, "a")); // => 1
}
private T1 Left<T1, T2>(T1 a, T2 b) { return a; }
}

The element type is also determined from a nested array like T[][].

using UnityEngine;
using Tsukimi;
public class OvInferNested : TsukimiBehaviour
{
void Start()
{
Debug.Log(Depth(new int[1][])); // => 1
}
private int Depth<T>(T[][] xs) { return xs.Length; }
}
using UnityEngine;
using Tsukimi;
public class OvBestCommon : TsukimiBehaviour
{
void Start()
{
long v = true ? 1 : 2L;
Debug.Log(v); // => 1
}
}
using UnityEngine;
using Tsukimi;
public class MemNestedType : TsukimiBehaviour
{
public struct Inner { public int V; }
void Start()
{
Inner i = new Inner();
Debug.Log(i.V); // => 0
}
}
using UnityEngine;
using Tsukimi;
public partial class R_part : TsukimiBehaviour
{
void Start()
{
Debug.Log(Helper());
}
}
public partial class R_part
{
private int extra;
int Helper()
{
extra = 41;
return extra + 1;
}
}
using UnityEngine;
using Tsukimi;
public abstract class MemAbstractBase : TsukimiBehaviour
{
public abstract int Get();
}
public class MemAbstractLeaf : MemAbstractBase
{
public override int Get() { return 1; }
void Start()
{
Debug.Log(Get()); // => 1
}
}

A virtual method can be overridden with override. The base type must be abstract.

using UnityEngine;
using Tsukimi;
public abstract class MemVirtualBase : TsukimiBehaviour
{
public virtual int Get() { return 0; }
}
public class MemVirtualLeaf : MemVirtualBase
{
public override int Get() { return 1; }
void Start()
{
Debug.Log(Get()); // => 1
}
}