Member declarations
Forms that compile (42)
Section titled “Forms that compile (42)”| Syntax | Description | Note |
|---|---|---|
private int count; | Field | |
private int count = 5; | Declaration with initializer | |
private const int Max = 10; | Constant | |
private static int Count; | Static field | The value is separate for each instance of the Behaviour and is not shared between instances. |
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 | set can’t be written inside a struct (struct and record). |
{ 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 | To override it in a derived type, make the base type abstract. |
public int this[int i] | Indexer | The value in the square brackets is passed as the accessor’s argument. |
this[0] = 3; | Writing to an indexer | value holds the value being written, and the index arrives as an argument. |
~C() { } | Finalizer | |
private volatile int V; | volatile | |
public readonly int Read() | readonly member of a value type | A value type is copied on every pass, so adding it changes nothing at runtime. |
Add(1) / Add(1, 2) | Selection by argument count | |
Show(1) / Show("a") | Selection by argument type | |
Show(int) beats Show(long) | Preference for the candidate with fewer conversions | |
Sum(1, 2) | Expansion into a variable-length argument | |
Sum(int,int) beats 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 : 2L | Common type from both sides of ?: | |
public struct Inner { ... } | Nested type | |
public partial class C | Type written in parts | |
abstract int Get(); / override | Implementing an abstract method | |
virtual int Get() / override | Overriding a default implementation | The base type may be either abstract or concrete. |
base.Ring() | Calling the base implementation | Only implementations in types you wrote can be called; the base of an event defined by the runtime cannot. |
Forms that don’t compile (11)
Section titled “Forms that don’t compile (11)”| Syntax | Description | Error | Reason | Alternative |
|---|---|---|---|---|
public class Base : TsukimiBehaviour ... public class Leaf : Base | Declaring two concrete types in the same file | TUKI0109 | by design | Split them into separate files (inheriting from a concrete type itself does work) |
public event System.Action Fired; | Declare an event | TUKI0108 | runtime | Call the method directly, or send it by specifying a name |
public event System.Action Fired { add { } remove { } } | write the add/remove accessors yourself | TUKI0108 | runtime | Same as above |
Fired?.Invoke() | Notify with event | TUKI0108 | runtime | Call the method directly, or send it by specifying a name |
Fired += Handler | Subscribe to event | TUKI0108, TUKI0099 | runtime | Same as above |
public static extern int Native() | Declare an extern implementation | TUKI0001 | runtime | Call a method on the runtime |
Read(int v) and Read(in int v) side by side | Distinguish candidates by how the argument is passed | TUKI0001 | undecided | Rename it. in alone can be used (Variables and argument passing) |
public int Twice { get { … } set { … } } (inside a struct) | A mutable computed property (value type) | TUKI0001 | not yet | Use get only, or make it a method (struct and record) |
public int Extra { get; set; } (inside a record) | Property (record) | TUKI0001 | by design | Use a positional parameter, or make it a method |
c[0] = 1; (an indexer inside a struct) | Writing to an indexer (value type) | TUKI0001 | not yet | Make it a method that returns a new value, or use an indexer on the Behaviour |
panel[0] (an indexer on another Behaviour) | Read an indexer on another Behaviour | TUKI0001 | undecided | Put a method on the other side and call it |
event | It can’t even be declared. Declaring, subscribing, and raising are all TUKI0108 |
~C() and volatile | They compile and run with neither an error nor a warning, but writing them changes no behaviour |
| Members that initialize the type itself | Static constructors and module initializers are both TUKI0001 (Field initialization and defaults) |
Whether a property is supported depends on where it lives and on which of the 3 accessor shapes it has.
| Where it’s placed | Auto-property | Computed (get only) | Computed (with set) |
|---|---|---|---|
| Behaviour | Compiles | Compiles | Compiles |
struct | Compiles | Compiles | Doesn’t compile |
record | init works | Compiles | Doesn’t compile |
| Interface | Compiles (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 }}Declaration with initializer
Section titled “Declaration with initializer”using UnityEngine;using Tsukimi;
public class MemFieldInitializer : TsukimiBehaviour{ private int count = 5;
void Start() { Debug.Log(count); // => 5 }}Constant
Section titled “Constant”using UnityEngine;using Tsukimi;
public class MemConst : TsukimiBehaviour{ private const int Max = 10;
void Start() { Debug.Log(Max); // => 10 }}Static field
Section titled “Static field”Read and written without creating an instance.
using UnityEngine;using Tsukimi;
public class MemStaticField : TsukimiBehaviour{ private static int Count;
void Start() { Count = 1; Debug.Log(Count); // => 1 }}Static readonly field
Section titled “Static readonly field”Read without creating an instance, and can’t be changed afterwards.
using UnityEngine;using Tsukimi;
public class MemStaticReadonly : TsukimiBehaviour{ private static readonly int Max = 10;
void Start() { Debug.Log(Max); // => 10 }}Methods
Section titled “Methods”Method declaration
Section titled “Method declaration”using UnityEngine;using Tsukimi;
public class MemMethod : TsukimiBehaviour{ private void Do() { Debug.Log(1); } // => 1
void Start() { Do(); }}Method with a return value
Section titled “Method with a return value”using UnityEngine;using Tsukimi;
public class MemMethodReturn : TsukimiBehaviour{ private int Get() { return 1; }
void Start() { Debug.Log(Get()); // => 1 }}Method with parameters
Section titled “Method with parameters”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 }}Default argument
Section titled “Default argument”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 }}Named argument
Section titled “Named argument”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 }}Overload
Section titled “Overload”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 }}Expression-bodied method
Section titled “Expression-bodied method”using UnityEngine;using Tsukimi;
public class MemExpressionBodied : TsukimiBehaviour{ private int Twice(int n) => n * 2;
void Start() { Debug.Log(Twice(2)); // => 4 }}Static method
Section titled “Static method”using UnityEngine;using Tsukimi;
public class MemStaticMethod : TsukimiBehaviour{ private static int Zero() { return 0; }
void Start() { Debug.Log(Zero()); // => 0 }}Properties
Section titled “Properties”Auto-property
Section titled “Auto-property”using UnityEngine;using Tsukimi;
public class MemAutoProperty : TsukimiBehaviour{ public int Value { get; set; }
void Start() { Value = 1; Debug.Log(Value); // => 1 }}Computed property
Section titled “Computed property”A property whose get body you write yourself.
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 }}Different accessibility for get and set
Section titled “Different accessibility for get and set”using UnityEngine;using Tsukimi;
public class MbAccessorAcc : TsukimiBehaviour{ void Start() { V = 1; Debug.Log(V); // => 1 }
public int V { get; private set; }}Property initializer
Section titled “Property initializer”using UnityEngine;using Tsukimi;
public class MbAutoPropInit : TsukimiBehaviour{ void Start() { Debug.Log(V); // => 1 }
public int V { get; set; } = 1;}Getter-only auto-property
Section titled “Getter-only auto-property”using UnityEngine;using Tsukimi;
public class MbGetterOnly : TsukimiBehaviour{ void Start() { Debug.Log(V); // => 0 }
public int V { get; }}init accessor
Section titled “init accessor”using UnityEngine;using Tsukimi;
public class MbInitAccessor : TsukimiBehaviour{ void Start() { Debug.Log(V); // => 0 }
public int V { get; init; }}Attribute on the backing field
Section titled “Attribute on the backing field”using UnityEngine;using Tsukimi;
public class MbFieldAttr : TsukimiBehaviour{ void Start() { Debug.Log(V); // => 0 }
[field: SerializeField] public int V { get; set; }}Virtual property
Section titled “Virtual property”A property marked virtual.
using UnityEngine;using Tsukimi;
public class MbVirtualProp : TsukimiBehaviour{ void Start() { Debug.Log(V); // => 1 }
public virtual int V { get { return 1; } }}Indexer
Section titled “Indexer”Declare this[int i] and values of that type can be read as v[i].
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 to an indexer
Section titled “Writing to an indexer”Write a set too and they can be written as v[i] = x.
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; } } }}Supported but with no effect
Section titled “Supported but with no effect”Finalizer
Section titled “Finalizer”The runtime has no mechanism for calling a finalizer, so the body never runs.
using UnityEngine;using Tsukimi;
public class MbFinalizer : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
~MbFinalizer() { }}volatile
Section titled “volatile”There are no concurrent flows of execution, so volatile doesn’t change what a read or write means.
using UnityEngine;using Tsukimi;
public class MbVolatile : TsukimiBehaviour{ void Start() { Debug.Log(V); // => 0 }
private volatile int V;}readonly member of a value type
Section titled “readonly member of a value type”It can go on a member that doesn’t modify the value.
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 }}Overload resolution
Section titled “Overload resolution”Selection by argument count
Section titled “Selection by argument count”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; }}Selection by argument type
Section titled “Selection by argument type”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; }}Expansion into a variable-length argument
Section titled “Expansion into a variable-length argument”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; }}Preference for the normal candidate
Section titled “Preference for the normal candidate”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; }}Omitting a default argument
Section titled “Omitting a default argument”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; }}Type parameter inference
Section titled “Type parameter inference”Inference from arguments
Section titled “Inference from arguments”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; }}Inference from array elements
Section titled “Inference from array elements”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]; }}Inference of two type parameters
Section titled “Inference of two type parameters”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; }}Inference from a nested array
Section titled “Inference from a nested array”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; }}Common type from both sides of ?:
Section titled “Common type from both sides of ?:”using UnityEngine;using Tsukimi;
public class OvBestCommon : TsukimiBehaviour{ void Start() { long v = true ? 1 : 2L; Debug.Log(v); // => 1 }}Inheritance and nesting
Section titled “Inheritance and nesting”Nested type
Section titled “Nested type”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 }}Type written in parts
Section titled “Type written in parts”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; }}Implementing an abstract method
Section titled “Implementing an abstract method”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 }}Overriding a default implementation
Section titled “Overriding a default implementation”A virtual method can be overridden with override.
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 }}Calling the base implementation
Section titled “Calling the base implementation”From the override, the implementation written in the base can be called with base.
using UnityEngine;using Tsukimi;
public abstract class MemBaseCallBase : TsukimiBehaviour{ public int hits; public virtual void Ring() { hits = hits + 1; }}
public class MemBaseCallLeaf : MemBaseCallBase{ public int extra;
public override void Ring() { base.Ring(); extra = extra + 1; }
void Start() { Ring(); Debug.Log(hits); // => 1 Debug.Log(extra); // => 1 }}