概要

前回(『プログラミング 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");  // こういうのができない。