カテゴリー: Tech

[C#]Genericsの使い方をまとめてみた。その1

はじめに

こんにちはsuzukiです。今回はGenericsの基本編です。例を交えて基本的な考え方をまとめました。
私は使う機会はそれなりにあるのですが、追加開発では初めから実装しないので書き方を忘れがちです。
今後の備忘として参考にできたらと思っています。
記事として少し長くなるため、2~3回でまとめさせていただく予定です。

Genericsとは

型をパラメーターとして受け取ることにより、その型に対応したクラスやメソッドを生成する機能です。
Genericsを使うことで型だけが異なり処理内容が同じクラスやメソッドを簡潔に書くことができます。

Genericsの例

今回は例としてSwapという処理をもとに考えていきます。

Genericsを使わない場合

例えば2つの整数の参照を入れ替えるという関数を記述すると下記のようになります。

   static void SwapInt(ref int a, ref int b)
    {
        //tempIntにaを一時的に代入
        int tempInt = a;
        //aにbを代入
        a = b;
        //bにtempIntを代入
        b = tempInt;
    }

SwapIntを利用し整数の参照を入れ替えるコードを記述すると下記になります

        int a = 5;
        int b = 10;
        Swap(ref a,ref b);
        Debug.Log("a = " + a);
        Debug.Log("b = " + b);

ログ出力結果

a = 10
b = 5

整数だけを扱う関数であれば問題ないのですが、同じ処理を文字列や小数でも行いたい場合にそれぞれの関数を作成するのは大変です。またコード量が増えるので当然保守性が下がってしまいます。

Genericsを使う場合

SwapIntはintの型のみしか使えない関数でしたが、Genericsを使うと型をパラメーターとして受け取ることができます。
使い方は関数名の後ろにを追加し受け取る型パラメーター名を設定します。Genericsを使う時によくと書かれていますが、Typeのイニシャルだかららしいです。特にでないといけない理由はありません。
また受け取ったTと同じ型で、a,bの参照とtempの型が設定されています。

    //型パラメーターとしてTを受け取る
    static void Swap<T>(ref T a, ref T b)
    {
        //受け取った型のtempにaを代入
        T temp = a;
        //aにbを代入
        a = b;
        //bにtempを代入
        b = temp;
    }

Swapを利用するには関数名(引数)のように呼び出しが可能です。
また、今回のように型パラメーターと引数の型が同じ場合を簡略化することが可能です。

        int a = 5;
        int b = 10;
        //型パラメータを指定して関数の呼び出し
        Swap<int>(ref a, ref b);
        Debug.Log("a = " + a + "  b = " + b);
        string str1 = "abc";
        string str2 = "def";
        //型パラメータは引数で明示的に与えれる場合は簡略化が可能
        Swap(ref str1, ref str2);
        Debug.Log("str1 = " + str1 + "  str2 = " + str2);

ログ出力結果

a = 10  b = 5
str1 = def  str2 = abc

Genericを使うことにより、”同じ任意のクラスのaとbの参照先を入れ替える”という抽象的な関数にすることができました。SwapIntに比べ様々なクラスを引数に設定できるようになり便利になりました。

Genericsのメリット

またGenericsを使うメリットが大きい例として、コレクションクラスと呼ばれる配列のような複数の値をひとまとめにするクラスの処理を考えていきましょう。
例)配列で行いたい処理

  • 中身がintだったら各要素の総和のintが返却される関数
  • 中身がfloatだったら各要素の総和のfloatが返却される関数
  • 中身がstringだったら各要素をつなげたstringが返却される関数

独自に行う場合foreachでそれぞれの要素(int,float,string)を+するという処理で実装可能です。しかしそれぞれ関数として定義するとコード量が多くなります。
またコレクションクラスとしても配列以外にも可変長配列などもあります。さらに総和以外の処理を追加することも考えられます。

上記をそれぞれ関数化するのではなく、Genericsを使い任意の型を格納し任意の種類のコレクションを扱えるコレクション操作関数を作成すれば、それぞれの関数を定義する必要がなくなるため、依存性も相関性も低い状態を作ることができます。
つまり雪だるま式に増えていくコードを書かなくても良くなります!

さいごに

今回は関数のGenericsを利用して説明させていただきました。他にもクラスを使う場合や制約条件の使い方等を次回にまとめさせていただきます。漫然と誰かが作ったGenericsを使うだけでなく、自分も途中からでも活用すべき内容はないか検討していきたいです。それではまた次回もよろしくお願いいたします。

おすすめ書籍

suzuki

シェア
執筆者:
suzuki
タグ: C#

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前