概要
前回(『プログラミング C#』 その3 型)のつづき。ジェネリックというのは特定のブランドに属さない、みたいな意味の言葉だ。 C# の話題で使うときは、特定の型に属さない型、と言い換えてよさそう。前回の記事で「型宣言 = クラス定義」ということがわかったから、特定のクラスに属さない型、と思ってもよさそうだ。
流し読みノート
ジェネリック型
<T>
こういうやつ。- 別名、型パラメータ、プレースホルダー。
- T を使っているのは単なる命名規則で、別のを使ってもエラーにはならない。
List<int>
とあるときint
の部分のことを型引数という。
具体例はこのとおり。
public class NamedContainer<T> // ジェネリック型を含むクラスだと明示。
{
public NamedContainer(T item) // item がジェネリック型。
{
Item = item;
}
public T Item { get; private set; }
}
var a = new NamedContainer<int>(99); // item を int 型にする。
var b = new NamedContainer<string>("Midori"); // item を string 型にする。
複数のインスタンス変数(C# 的にいうとフィールド)を異なる型にしたいときどうするのかは知らん。
制約
型引数には「制約」をつけられる。
where T: IComparable<T>
みたいなのが型制約。where T: class
みたいなのが参照型制約。int
double
struct
といった値型は使えなくなる。前回覚えたint?
も使えない。where T: struct
はこの逆で値型制約。where T: IEnumerable<T>, IDisposable, new()
みたいに書けば複数制約設定可能。 new がなんだったかは忘れた。
こんなふうに書く。
public class Foo<T>
where T : class
ジェネリックメソッド
あー、そうそうコレだよ欲しかったのは。どんな型も受け入れ OK なメソッド。クラスよりこっちのほうが身近でいい。
// T GetLast : 返却値をジェネリック型で定義。
// <T> : ジェネリック型を含むメソッドであると明示。
// T[] items : どんな型の配列でも OK。
public static T GetLast<T>(T[] items)
{
return items[items.Length - 1];
}
// 呼び出し方。 int で使うなら……
int[] values = { 1, 2, 3 };
int last = GetLast<int>(values);
ジェネリックメソッドにも上述した制約はつけられる。
てか C# の型指定見づらいな。 Python のほうが見やすくない?
def get_last(items: object) -> object:
return items[-1]
last = get_last([1, 2, 3])
小休憩
ジェネリック型導入前は、こうした汎用型には object
型を使っていたそうな。だけどそれでは次のような面倒があった。
object
をいちいちキャストしないといけなかった。<T>
ならコンパイル時に自動で型が変わるからラク。- 値型を効率的に対処できなかった。(これは意味不明。)
ともあれジェネリック型導入により List<T>
なんていう便利な型を使えるのだ。ありがたや。いや、てか普通の配列が不便すぎるだろ。
string[] values = { "foo", "bar" };
values.Add("baz"); // こういうのができない。