ジェネリクス
書ける形(29)
Section titled “書ける形(29)”| 書き方 | 説明 |
|---|---|
T First<T>(T a, T b) | 型引数を持つメソッド |
First(3, 4) | 型引数の推論 |
T1 Pick<T1, T2>(T1 a, T2 b) | 2 つの型引数 |
int Count<T>(T[] xs) | 型引数の配列 |
default(T) | 型引数の既定値 |
struct Boxed<T> { public T V; } | 型引数を持つ struct |
record Boxed<T>(T V); | 型引数を持つ record |
Boxed<Boxed<int>> | 型引数の入れ子 |
public Boxed<int> B; | 別の型のフィールド |
class Box<T> { … } | 型引数を持つ class の宣言 |
interface IProducer<T> | 型引数を持つインタフェースの宣言 |
delegate T Maker<T>(); | 型引数を持つデリゲートの宣言 |
where T : struct | 値型への限定 |
where T : class | 参照型への限定 |
where T : notnull | null にならない型への限定 |
where T : unmanaged | 参照を含まない値型への限定 |
where T : System.Enum | 列挙型への限定 |
where T : System.Delegate | デリゲートへの限定 |
where T : IShape | インタフェースを実装した型への限定 |
v.Get() | インタフェースの制約を通した呼び出し |
where T : Root | 基底クラスの派生への限定 |
where T : U | 別の型引数へ変換できる型への限定 |
where T : new() | 引数なしで作れる型への限定 |
T x = new T(); | 型引数の値の生成 |
where T : struct, IThing | 制約の列挙 |
object o = v; o is int | 型引数の値に型を問う |
abstract class Base<T> : TsukimiBehaviour | 型引数を持つ基底の Behaviour |
interface IProducer<out T> | 取り出す向きにだけ使う型引数 |
interface IConsumer<in T> | 渡す向きにだけ使う型引数 |
書けない形(11)
Section titled “書けない形(11)”| 書き方 | 説明 | エラー | 理由 | 代わりに |
|---|---|---|---|---|
Box<int> b = new Box<int>(); | 型引数を持つ class を使う | TUKI0001 | 環境 | struct か record にする(そちらは使える) |
IProducer<int> p = new IntProducer(); | 型引数を持つインタフェースを使う | TUKI0001 | 設計 | 型引数を使わないインタフェースにする |
v.V(where T : Root を通して) | 基底クラスの制約を通してメンバに触る | TUKI0001 | 環境 | インタフェースの制約に変える(そちらはメソッドを呼べる) |
Make<Holder>()(where T : new() に class を渡す) | 参照型を型引数にする | TUKI0001 | 環境 | struct か record を渡す |
typeof(T) | 型引数の型そのものを取る | TUKI0001 | 未定 | 型引数を使わない形にする |
o is Bead(型引数の値を入れた object に対して) | 上の 14 個に無い型を問う | TUKI0001 | 未実装 | union かインタフェースで表す |
int Id<T>(T v) { … }(メソッドの中) | その場で型引数つきの関数を書く | TUKI0001 | 未定 | メソッドとして書く |
Outer<int>.Inner | 型引数を持つ型の中の型 | TUKI0001 | 環境 | 入れ子にしない |
where T : class? | null になりうる参照型への限定 | TUKI0201 | 設計 | where T : class にする(この形だけは null 安全の検査が反応する) |
class Leaf<U> : Base<U> | 型引数を渡したまま基底を継承する | TUKI0001 | 未実装 | 派生側で型を決める(class Leaf : Base<int>) |
public class C<T> : TsukimiBehaviour | 型引数を持つ Behaviour | TUKI0001 | 未実装 | 型引数を使わない形にする(1 つのプログラムは 1 つの具体的な型になる) |
| 宣言できることと、使えることは別です。型引数を持つ class・インタフェース・デリゲートは、宣言までは通って、使った行で断られます。型引数を持つ struct と record なら使えます。 |
制約は「宣言」と「制約を通して何ができるか」で割れます。
| 制約 | 宣言 | 通してできること |
|---|---|---|
where T : IShape | 通る | メソッドを呼べる |
where T : Root(基底クラス) | 通る | メンバに触ると断られる |
where T : new() | 通る | new T() で値を作れる |
型引数は、Behaviour そのものには付けられません。1 つのプログラムは 1 つの具体的な型で、展開先を決められないためです。基底には付けられます。継承した時点で型が決まるので、展開先が 1 通りに定まるためです。
型引数を持つメソッド
Section titled “型引数を持つメソッド”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; }}型引数の推論
Section titled “型引数の推論”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 つの型引数
Section titled “2 つの型引数”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; }}型引数の配列
Section titled “型引数の配列”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; }}型引数の既定値
Section titled “型引数の既定値”using UnityEngine;using Tsukimi;
public class GnDefaultOfT : TsukimiBehaviour{ void Start() { Debug.Log(Zero<int>()); // => 0 }
private T Zero<T>() { return default(T); }}型引数を持つ struct
Section titled “型引数を持つ struct”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 }}型引数を持つ record
Section titled “型引数を持つ record”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 }}型引数の入れ子
Section titled “型引数の入れ子”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 }}別の型のフィールド
Section titled “別の型のフィールド”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 }}型引数を持つ class の宣言
Section titled “型引数を持つ class の宣言”宣言はできますが、使うと断られます(書けない形)。
using UnityEngine;using Tsukimi;
public class Box<T> { public T V; }public class G2ClassDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}型引数を持つインタフェースの宣言
Section titled “型引数を持つインタフェースの宣言”インタフェースにも型引数を付けられます。こちらも使うと断られます(書けない形)。
using UnityEngine;using Tsukimi;
public interface IProducer<T> { T Make(); }public class GcGenericInterface : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}型引数を持つデリゲートの宣言
Section titled “型引数を持つデリゲートの宣言”using UnityEngine;using Tsukimi;
public delegate T Maker<T>();public class GcGenericDelegate : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}制約(型の種類)
Section titled “制約(型の種類)”値型への限定
Section titled “値型への限定”int や自分で作った struct だけを受け付けます。
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; }}参照型への限定
Section titled “参照型への限定”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; }}null にならない型への限定
Section titled “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; }}参照を含まない値型への限定
Section titled “参照を含まない値型への限定”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; }}列挙型への限定
Section titled “列挙型への限定”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; }}デリゲートへの限定
Section titled “デリゲートへの限定”制約は宣言できます。この環境ではデリゲートの値を作れません。
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; }}制約(型の関係)
Section titled “制約(型の関係)”インタフェースを実装した型への限定
Section titled “インタフェースを実装した型への限定”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 }}インタフェースの制約を通した呼び出し
Section titled “インタフェースの制約を通した呼び出し”インタフェースの制約を通すと、そのメソッドを呼べます。基底クラスの制約では呼べません(書けない形)。
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(); }}基底クラスの派生への限定
Section titled “基底クラスの派生への限定”宣言はできますが、そのメンバに触ると断られます(書けない形)。
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; }}別の型引数へ変換できる型への限定
Section titled “別の型引数へ変換できる型への限定”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; }}引数なしで作れる型への限定
Section titled “引数なしで作れる型への限定”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; }}型引数の値の生成
Section titled “型引数の値の生成”この制約を通すと、型引数の値を作れます。この環境は値型の引数なしコンストラクタを公開していないので、できあがる値は 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; }}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(); }}型引数と実行時の型
Section titled “型引数と実行時の型”型引数の値に型を問う
Section titled “型引数の値に型を問う”型引数の値を object に入れてから、決まった型かどうかを問えます。問える型は bool byte sbyte short ushort char int uint long ulong float double decimal string の 14 個で、それ以外は書けません(書けない形)。
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; }}Behaviour への適用
Section titled “Behaviour への適用”型引数を持つ基底の Behaviour
Section titled “型引数を持つ基底の Behaviour”基底に型引数を付けられます。継承した時点で型が決まるので、派生側は型引数を持ちません。
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 }}変性(宣言)
Section titled “変性(宣言)”取り出す向きにだけ使う型引数
Section titled “取り出す向きにだけ使う型引数”using UnityEngine;using Tsukimi;
public interface IProducer<out T> { T Make(); }public class GcCovarianceDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}渡す向きにだけ使う型引数
Section titled “渡す向きにだけ使う型引数”using UnityEngine;using Tsukimi;
public interface IConsumer<in T> { void Take(T v); }public class GcContravarianceDecl : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }}