ジェネリック
対応(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(); | 型引数の値の生成 | この環境は値型の引数なしコンストラクタを公開していないので、できあがる値は default(T) と同じです。 |
where T : struct, IThing | 制約の列挙 | |
object o = v; o is int | 型引数の値に型を問う | 問える型は bool byte sbyte short ushort char int uint long ulong float double decimal string の 14 個で、それ以外は書けません(非対応)。 |
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 なら使えます) |
制約を通して何ができるかは 3 通りで、「宣言」と「制約を通してできること」で割れます。
| 制約 | 宣言 | 通してできること |
|---|---|---|
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 “型引数の値の生成”この制約を通すと、型引数の値を作れます。
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 へ代入してから、決まった型かどうかを問えます。
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 }}