C#
 Computer >> コンピューター >  >> プログラミング >> C#

C#のジェネリックとは?仕組みと使い方を実例付きで徹底解説

C# 2.0で導入されたジェネリックは、この言語における最も重要な概念のひとつです。ジェネリックを活用することで、コンパイル時に型安全性が保証された、再利用性とパフォーマンスに優れたコードを書けるようになります。つまり、事前に型を知らなくても、その型をコードの中で柔軟に扱えるのです。

ジェネリックは.NETフレームワークの随所で活用されており、代表例としてはコレクション、デリゲート、非同期処理などが挙げられます。ジェネリックを使えば、コレクションのサイズを事前に把握する必要はなく、任意の要素型に対して利用できます。もちろん、自分のコードで定義したカスタムデータ型も問題なく扱えます。C#では、クラスやインターフェースなどの「ジェネリック型」と「ジェネリックメソッド」の両方がサポートされています。

ジェネリックには「型パラメータ」と「型引数」という2つの重要な概念があります。これは、メソッドがパラメータを受け取り、呼び出し側が引数を渡す仕組みと非常によく似ています。

ジェネリック型の基本

ジェネリック型を宣言する構文は、型名の後に山かっこ(< >)で囲んだ型パラメータを記述します。次の例では、Locator<T> がジェネリッククラスです。

public class Locator<T>
{

}

Locator<T> のインスタンスを作成するときは、newキーワードに続けてクラス名を指定します。ただし、Tの部分には実際に渡したい型を指定します。以下の例では、string型を型引数として渡しています。

var stringLocator = new Locator<string>();

型パラメータ(T)は、クラスのメソッド内でも使用できます。次の例をご覧ください。

public class Locator<T>
{
    public IList<T> Items { get; set; }

    public T Locate(int index)
    {
        return Items[index];
    }
}

var stringLocator = new Locator<string>();
string item = stringLocator.Locate(2);

IntelliSenseによる型安全性の向上

ジェネリックのもうひとつの大きなメリットは、エディタによるIntelliSenseのサポートです。Visual StudioやVS Codeで stringLocator.Locate(4) と入力し、メソッド名にカーソルを合わせると、戻り値が抽象的なTではなくstring型であることが表示されます。もし結果をstring以外の型に代入しようとすると、コンパイラがエラーを検出します。

// エラー: 型 'string' を 'int' に暗黙的に変換できません (CS0029)
int item = stringLocator.Locate(2);

このように、ジェネリックは実行時エラーを未然に防ぎ、開発段階でバグを発見しやすくしてくれます。

また、ジェネリック型は、ジェネリック基底クラスやジェネリックインターフェースを継承する際にも、型パラメータを型引数として使えます。たとえば、LinkedList<T> 型は IEnumerable<T> をはじめとする複数のジェネリックインターフェースを実装しています。

public class LinkedList<T> : IEnumerable<T>

ジェネリックメソッドの使い方

ジェネリックメソッドとは、メソッド自身が型パラメータを宣言し、それをメソッド内部やパラメータ・戻り値の型として利用できるメソッドのことです。次の例では、Swap<T> がT型のパラメータを2つ受け取り、T型のインスタンスを返すジェネリックメソッドです。

public class Swapper
{
    public T Swap<T>(T first, T second)
    {
        T temp = first;
        first = second;
        second = temp;
        return temp;
    }
}

ジェネリック型と同様に、ジェネリックメソッドを呼び出すと、厳密に型指定された変数が返されます。

var swapper = new Swapper();
int result = swapper.Swap<int>(5, 3);

ジェネリックパラメータは複数持つことも可能です。System.Collections.Generic 名前空間の Dictionary クラスは、キーと値のために2つの型パラメータを持っています。

public class Dictionary<TKey, TValue>

ジェネリックにできるもの・できないもの

最後に、何がジェネリックの対象になるのかを正しく理解しておきましょう。型については、列挙型(enum)以外はすべてジェネリックにできます。具体的な対象は以下の通りです。

  • クラス
  • 構造体(struct)
  • インターフェース
  • デリゲート

一方、型のメンバーに関しては、メソッドと入れ子の型だけがジェネリックになれます。以下のメンバーはジェネリックにできない点に注意してください。

  • フィールド
  • プロパティ
  • インデクサー
  • コンストラクター
  • イベント
  • ファイナライザー

サンプルコード全体

ここまでの内容をまとめた完全なサンプルプログラムがこちらです。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var stringLocator = new Locator<string>
        {
            Items = new string[] { "JavaScript", "CSharp", "Golang" }
        };
        string item = stringLocator.Locate(1);
        Console.WriteLine(item); // CSharp

        var swapper = new Swapper();
        int a = 5, b = 3;
        int result = swapper.Swap<int>(ref a, ref b);
        Console.WriteLine($"a = {a}, b = {b}");
    }
}

public class Locator<T>
{
    public IList<T> Items { get; set; }

    public T Locate(int index)
    {
        return Items[index];
    }
}

public class Swapper
{
    public T Swap<T>(ref T first, ref T second)
    {
        T temp = first;
        first = second;
        second = temp;
        return temp;
    }
}

実行結果

CSharp
a = 3, b = 5

このように、ジェネリックを使いこなすことで、型安全かつ再利用性の高いC#コードを効率的に書けるようになります。日常的な開発でぜひ活用してみてください。

  1. Windows 10でファイルの種類(拡張子)を変更する方法【初心者向け完全ガイド】

    MP3、ZIP、PDFといったファイルを目にするとき、私たちが実際に見ているのは「ファイルタイプ」、つまり拡張子です。拡張子は、OSがそのファイルが何であるか、そしてどのソフトウェアで開くべきかを判別するための重要な目印となります。WindowsやmacOSはこの拡張子をもとに、MP3ファイルなら音楽プレーヤーで、PDFならPDFリーダーで開く、という判断を行っています。しかし、ファイルの拡張子が正しくない場合、Windowsは誤ったプログラムでファイルを開こうとし、エラーが発生してファイルにアクセスできなくなることがあります。Microsoft WordでMP3ファイルを開けないのと同じ理屈

  2. 【初心者向け】PCでNATタイプを変更する4つの方法|Windows 10対応

    21世紀の現在、高速なインターネット接続は生活に欠かせないインフラとなっています。多くの人々が通信プランや機器のアップグレードに多額の費用をかけていますが、それでもネット速度が遅い原因が分からず悩んでいる方は少なくありません。もし接続速度を改善できずにお困りなら、PCのNATタイプを変更することを検討してみましょう。 PCでNATタイプを変更する方法 NATとは? 誰もが日常的にインターネットを利用していますが、その接続を支えるバックグラウンドの仕組みまで理解している人は多くありません。その一つがNAT(Network Address Translation:ネットワークアドレス変換)です。