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 | Instead |
|---|---|---|---|---|
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(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>()(where T : new() に class を渡す) | 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(型引数の値を入れた object に対して) | Asking about a type not in the 14 above | TUKI0001 | not yet | Represent it with a union or interface |
int Id<T>(T v) { … }(メソッドの中) | 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? | Restricted 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 different things. A class, interface, or delegate with a type argument compiles up through the declaration, but the line that uses it is rejected. A struct or record with a type argument can be used. |
Constraints split into “declaration” and “what you can do through the constraint.”
| Constraint | Declaration | What you can do through it |
|---|---|---|
where T : IShape | Compiles | Can call the method |
where T : Root (base class) | Compiles | Rejected when you touch a member |
where T : new() | Compiles | Can create a value with new T() |
A type argument can’t be attached to the behaviour itself. One program is one concrete type, so there’s no expansion target to decide. It can be attached to the base class: the type is fixed at the point of inheritance, so the expansion target is fixed 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”The declaration compiles, but using it is rejected (Forms that don’t compile).
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”An interface can also have a type argument. Using it is rejected too (Forms that don’t compile).
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)”Restricted to value types
Section titled “Restricted 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; }}Restricted to reference types
Section titled “Restricted 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; }}Restricting to types that cannot be null
Section titled “Restricting 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; }}Restricted to value types without references
Section titled “Restricted 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; }}Restricted to enum types
Section titled “Restricted 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; }}Restricted to delegates
Section titled “Restricted to delegates”The constraint compiles. This runtime can’t create a delegate value.
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)”Restricted to types that implement an interface
Section titled “Restricted 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”An interface constraint lets you call its method. A base class constraint doesn’t (Forms that don’t compile).
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(); }}Restricted to derived types of a base class
Section titled “Restricted to derived types of a base class”The declaration compiles, but touching its member is rejected (Forms that don’t compile).
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; }}Restricted to types convertible to another type argument
Section titled “Restricted 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; }}Restricted to types that can be created without arguments
Section titled “Restricted 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”This constraint lets you create a value of the type argument. This runtime doesn’t expose a value type’s parameterless constructor, so the resulting value is the same as default(T).
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”Put the type argument’s value into object, then you can ask whether it’s a fixed type. The types you can ask about are these 14: bool byte sbyte short ushort char int uint long ulong float double decimal string. Anything else doesn’t compile (Forms that don’t compile).
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”You can put a type argument on the base class. The type is fixed at the point of inheritance, so the derived side doesn’t have a type argument.
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 }}