up:: Csharp
ジェネリックメソッド
一言で言うと、型も引数で指定できるメソッド。
様々な値型を渡して処理させたいときに使う機能。クラスで使うときもコンストラクタで指定するので結局メソッド。
使い方は
戻り値型 メソッド名<型引数>(){
}
とするだけ。型引数で()内の引数の型を指定することで、引数の型が何だろうと型引数で扱うことができる。
型引数は,で区切れば複数指定できる。
注意
そのままでは何の型なのかわからないせいで、型固有の能力が使えない。具体的には算術演算子などが使えない。
ジェネリック型制約
その対策になるのがこれ。
void Test<T>(T x) where T : インターフェイス{
}
として、型引数の型を同じインターフェイスを継承したもののみに制限する。これも,分けで複数指定可。普通のインターフェイス継承みたいに使える。
また、型引数ごとに指定するので、長い場合は改行推奨。
static void Test<T1, T2>(T1 x, T2 y)
where T1 : IComparable
where T2 : ICloneable
{
Console.WriteLine(x);
Console.WriteLine(y);
}
等価を評価したい場合はEqualsを使う。全てのクラスはObject型から派生し、Object型はEqualsを持っているので使える。
基本的にこの中で**==や!=は使えない。**
なお、比較したい場合は型制約が必要。
Where
型指定のために使う句。これがあると型固有の機能が使えるようになる。……が、これ自体の制約が厳しい。そもそも型で縛ろうとすると一つしか指定できないというのがつらい。
使える制約
制約 | 説明 |
---|---|
where T : struct | 値型のみ 値型と参照型参照 他の型制約と併用する場合、この型制約は最初に記述する |
where T : class | 参照型のみ 値型と参照型参照 他の型制約と併用する場合、この型制約は最初に記述する |
where T : クラス名 | 指定したクラス、またはその派生クラス |
where T : インターフェイス名 | 指定したインターフェイスクラスを継承するクラス |
where T : new() | 引数なしのコンストラクターを持つクラス 他の型制約と併用する場合、この型制約を最後に指定する |
where T : unmanaged | アンマネージ型 値型をポインタで扱う場合の型 |
よく使うやつ
- IComparable
比較したいときに使う。>や<は使えないので、比較には.CompareToを使う。
ジェネリック - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
オブジェクトの比較(C#) - 超初心者向けプログラミング入門
IEquatableを完全に理解する - Qiita
特定のメソッドを含んだクラスを指定したい
そんなものはない。 特定のメソッド持ちクラス全てを指定しようにも、classを指定しているという点で重複しているのかコンパイルが通らない。
おとなしく指定は諦めよう。
Rigidbody2Dを指定したい
Cannot use sealed class 'UnityEngine.Rigidbody2D' as type parameter constraint
というエラーが出る。
派生を禁じてると、型指定が一つの型しか指定できない関係上、ジェネリックで受けるいろんな型というのがそもそも存在しなくなる。 それはジェネリックじゃなくていいよね、ということで禁止されてる。
c#-なぜ封印されたクラスをジェネリック制約として使用できないのですか? - スタックオーバーフロー
Object型配列でよくね?
Object型配列と比べると、代入した後に別の値型を代入できない。また、別の値型を一つの配列にまとめることもできない。
静的型付けとしては、途中でint配列がstring配列にすり替わったりしないので助かる。助かるというか、値型が一定してないとforeachなんかでエラーになる。
ちなみにObjectに何でも入るのは、あらゆるクラスがObject型をもとに定義されているから。このことをあらゆるクラスはObject型のサブタイプであるという。このため、stringもintも同じくObjectに突っ込める。
また、Objectの場合は取り出す際にキャストが要る。 そりゃ、Object相手には何したらいいかわからないし。
ジェネリック・クラスで変わるC#とVBのコレクション:特集C#&VBジェネリック超入門(前編)(1/4 ページ) - @IT