Generics
Forms that compile (29)
Section titled “Forms that compile (29)”Forms that don’t compile (11)
Section titled “Forms that don’t compile (11)”| Syntax | Description | Error | Reason | Alternative |
|---|---|---|---|---|
Box<int> b = new Box<int>(); | Using a class with a type argument | TUKI0001 | runtime | Use a struct or record instead (that works) |
IProducer<int> p = new IntProducer(); | Using an interface with a type argument | TUKI0001 | by design | Use an interface without a type argument |
v.V (through where T : Root) | Touching a member through a base class constraint | TUKI0001 | runtime | Change to an interface constraint (that lets you call the method) |
Make<Holder>() (passing a class to where T : new()) | Using a reference type as the type argument | TUKI0001 | runtime | Pass a struct or record |
typeof(T) | Getting the type argument’s type itself | TUKI0001 | undecided | Use a form without a type argument |
o is Bead (against an object holding the type argument’s value) | Asking about a type not in the 14 above | TUKI0001 | not yet | Represent it with a union or interface |
int Id<T>(T v) { … } (inside a method) | Writing a function with a type argument inline | TUKI0001 | undecided | Write it as a method |
Outer<int>.Inner | A type inside a type with a type argument | TUKI0001 | runtime | Don’t nest them |
where T : class? | Constrained to nullable reference types | TUKI0201 | by design | Use where T : class (only this form triggers the null-safety check) |
class Leaf<U> : Base<U> | Inheriting a base class while passing the type argument through | TUKI0001 | not yet | Decide the type on the derived side (class Leaf : Base<int>) |
public class C<T> : TsukimiBehaviour | A behaviour with a type argument | TUKI0001 | not yet | Use a form without a type argument (one program becomes one concrete type) |
| Declaring and using are separate | Classes, interfaces, and delegates with type parameters are supported as far as their declaration, and error on the line that uses them (structs and records with type parameters are usable) |
There are 3 kinds of what you can do through a constraint, split into “declaration” and “what you can do through the constraint.”
| Constraint | Declaration | What you can do through it |
|---|---|---|
where T : IShape | Forms that compile | Can call the method |
where T : Root (base class) | Forms that compile | Touching a member errors |
where T : new() | Forms that compile | Can create a value with new T() |
| Type parameters on a Behaviour | They can’t go on the Behaviour itself (one program is one concrete type, and the expansion target can’t be decided). They can go on the base, where inheritance fixes the type and the expansion target resolves to exactly one |
Methods
Section titled “Methods”A method with a type argument
Section titled “A method with a type argument”using UnityEngine;using Tsukimi;
public class GnMethod : TsukimiBehaviour{ void Start() { Debug.Log(First(3, 4)); // => 3 }
private T First<T>(T a, T b) { return a; }}Type parameter inference
Section titled “Type parameter inference”using UnityEngine;using Tsukimi;
public class GnInference : TsukimiBehaviour{ void Start() { int n = First(3, 4); float f = First(1f, 2f); Debug.Log(n + f); // => 4 }
private T First<T>(T a, T b) { return a; }}2 type arguments
Section titled “2 type arguments”using UnityEngine;using Tsukimi;
public class GnTwoParameters : TsukimiBehaviour{ void Start() { Debug.Log(Pick(1, "a")); // => 1 }
private T1 Pick<T1, T2>(T1 a, T2 b) { return a; }}An array of a type argument
Section titled “An array of a type argument”using UnityEngine;using Tsukimi;
public class GnArrayParameter : TsukimiBehaviour{ void Start() { int[] a = new int[] { 1, 2 }; Debug.Log(Count(a)); // => 2 }
private int Count<T>(T[] xs) { return xs.Length; }}Default value of a type argument
Section titled “Default value of a type argument”using UnityEngine;using Tsukimi;
public class GnDefaultOfT : TsukimiBehaviour{ void Start() { Debug.Log(Zero<int>()); // => 0 }
private T Zero<T>() { return default(T); }}A struct with a type argument
Section titled “A struct with a type argument”using UnityEngine;using Tsukimi;
public struct Boxed<T>{ public T V;}
public class GnGenericStruct : TsukimiBehaviour{ void Start() { Boxed<int> b = new Boxed<int>(); b.V = 1; Debug.Log(b.V); // => 1 }}A record with a type argument
Section titled “A record with a type argument”using UnityEngine;using Tsukimi;
public record Boxed<T>(T V);
public class GnGenericRecord : TsukimiBehaviour{ void Start() { Boxed<int> b = new Boxed<int>(1); Debug.Log(b.V); // => 1 }}Nested type arguments
Section titled “Nested type arguments”using UnityEngine;using Tsukimi;
public struct Boxed<T>{ public T V;}
public class GnNestedArgument : TsukimiBehaviour{ void Start() { Boxed<Boxed<int>> b = new Boxed<Boxed<int>>(); Debug.Log(b.V.V); // => 0 }}A field of another type
Section titled “A field of another type”using UnityEngine;using Tsukimi;
public struct Boxed<T>{ public T V;}
public struct Holder{ public Boxed<int> B; public int Get() { return B.V; }}
public class GnGenericTypeInStruct : TsukimiBehaviour{ void Start() { Holder h = new Holder(); Debug.Log(h.Get()); // => 0 }}Declaring a class with a type argument
Section titled “Declaring a class with a type argument”It can be declared, but using it errors (Forms that don’t compile (11)).
using UnityEngine;using Tsukimi;
public class Box<T> { public T V; }public class G2ClassDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}Declaring an interface with a type argument
Section titled “Declaring an interface with a type argument”Interfaces can take type parameters too.
using UnityEngine;using Tsukimi;
public interface IProducer<T> { T Make(); }public class GcGenericInterface : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}Declaring a delegate with a type argument
Section titled “Declaring a delegate with a type argument”using UnityEngine;using Tsukimi;
public delegate T Maker<T>();public class GcGenericDelegate : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}Constraints (kind of type)
Section titled “Constraints (kind of type)”Constrained to value types
Section titled “Constrained to value types”Accepts only int or a struct you wrote yourself.
using UnityEngine;using Tsukimi;
public class GnConstraintStruct : TsukimiBehaviour{ void Start() { Debug.Log(Copy(1)); // => 1 }
private T Copy<T>(T v) where T : struct { return v; }}Constrained to reference types
Section titled “Constrained to reference types”using UnityEngine;using Tsukimi;
public class GcClass : TsukimiBehaviour{ void Start() { Debug.Log(Take<string>("a")); // => 1 }
private int Take<T>(T v) where T : class { return 1; }}Constraining to types that cannot be null
Section titled “Constraining to types that cannot be null”using UnityEngine;using Tsukimi;
public class GcNotNull : TsukimiBehaviour{ void Start() { Debug.Log(Take<int>(1)); // => 1 }
private int Take<T>(T v) where T : notnull { return 1; }}Constrained to value types without references
Section titled “Constrained to value types without references”using UnityEngine;using Tsukimi;
public class GcUnmanaged : TsukimiBehaviour{ void Start() { Debug.Log(Take<int>(1)); // => 1 }
private int Take<T>(T v) where T : unmanaged { return 1; }}Constrained to enum types
Section titled “Constrained to enum types”using UnityEngine;using Tsukimi;
public enum Mode { Off, On }public class GcEnum : TsukimiBehaviour{ void Start() { Debug.Log(Take<Mode>(Mode.On)); // => 1 }
private int Take<T>(T v) where T : System.Enum { return 1; }}Constrained to delegates
Section titled “Constrained to delegates”The constraint can be declared.
using UnityEngine;using Tsukimi;
public class GcDelegate : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
private int Take<T>(T v) where T : System.Delegate { return 1; }}Constraints (type relationships)
Section titled “Constraints (type relationships)”Constrained to types that implement an interface
Section titled “Constrained to types that implement an interface”using UnityEngine;using Tsukimi;
public interface IShape{ float Area();}
public struct Box : IShape{ public float Area() { return 1f; }}
public struct Wrap<T> where T : IShape{ public T V;}
public class GnConstraintInterface : TsukimiBehaviour{ void Start() { Wrap<Box> w = new Wrap<Box>(); Debug.Log(w.V.Area()); // => 1 }}Calling through an interface constraint
Section titled “Calling through an interface constraint”Going through an interface constraint lets you call its methods.
using UnityEngine;using Tsukimi;
public interface IThing { int Get(); }public class G2IfaceMember : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
private int Take<T>(T v) where T : IThing { return v.Get(); }}Constrained to derived types of a base class
Section titled “Constrained to derived types of a base class”It can be declared, but touching its members errors (Forms that don’t compile (11)).
using UnityEngine;using Tsukimi;
public class Root { public int V; }public class Leaf : Root { }public class G2BaseDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
private int Take<T>(T v) where T : Root { return 1; }}Constrained to types convertible to another type argument
Section titled “Constrained to types convertible to another type argument”using UnityEngine;using Tsukimi;
public interface IThing { int Get(); }public struct Impl : IThing { public int Get() { return 1; } }
public class GcTypeParam : TsukimiBehaviour{ void Start() { Debug.Log(Take<Impl, Impl>(new Impl())); // => 1 }
private int Take<T, U>(T v) where T : U where U : IThing { return 1; }}Constrained to types that can be created without arguments
Section titled “Constrained to types that can be created without arguments”using UnityEngine;using Tsukimi;
public class Holder { public int V; }public class G2NewDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
private int Take<T>() where T : new() { return 1; }}Creating a value of a type argument
Section titled “Creating a value of a type argument”Going through this constraint lets you create a value of the type argument.
using UnityEngine;using Tsukimi;
public struct Bead{ public int V;}
public class R_cnew : TsukimiBehaviour{ void Start() { Debug.Log(Make<Bead>().V); // => 0 }
private T Make<T>() where T : new() { T x = new T(); return x; }}Multiple constraints
Section titled “Multiple constraints”using UnityEngine;using Tsukimi;
public interface IThing { int Get(); }public struct Impl : IThing { public int Get() { return 1; } }
public class GcMultiple : TsukimiBehaviour{ void Start() { Debug.Log(Take<Impl>(new Impl())); // => 1 }
private int Take<T>(T v) where T : struct, IThing { return v.Get(); }}Type arguments and runtime types
Section titled “Type arguments and runtime types”Asking the type of a type argument’s value
Section titled “Asking the type of a type argument’s value”Assign the type argument’s value to object and you can then test it against a fixed type.
using UnityEngine;using Tsukimi;
public class GcIsOnT : TsukimiBehaviour{ void Start() { Debug.Log(Check<int>(1)); // => true }
private bool Check<T>(T v) { object o = v; return o is int; }}Applying to a behaviour
Section titled “Applying to a behaviour”A base behaviour with a type argument
Section titled “A base behaviour with a type argument”The base can take type parameters.
using UnityEngine;using Tsukimi;
public abstract class R_gbase<T> : TsukimiBehaviour{ public T V;}
public class R_gleaf : R_gbase<int>{ void Start() { V = 1; Debug.Log(V); // => 1 }}Variance (declaration)
Section titled “Variance (declaration)”A type argument used only in output position
Section titled “A type argument used only in output position”using UnityEngine;using Tsukimi;
public interface IProducer<out T> { T Make(); }public class GcCovarianceDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}A type argument used only in input position
Section titled “A type argument used only in input position”using UnityEngine;using Tsukimi;
public interface IConsumer<in T> { void Take(T v); }public class GcContravarianceDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}