注目キーワード
  1. プログラミング
  2. C#

【C#】インデクサの使い方

C#

はじめに

こうちゃ
こんにちは、こうちゃです。
以前、以下の記事で「演算子のオーバーロード」の紹介をしました。
関連記事

はじめに こうちゃ こんにちは、こうちゃです。 みなさんは、C#を扱っていて、「インスタンス同士で演算できたら良いのにな〜」と思ったことはありませんか? 私はあります。(笑) ということで今回は、C#で演算子をオー[…]

インスタンス同士の演算ができるよ〜 という内容でしたね。
同様に、インスタンスに対して、配列のようにアクセスが可能な インデクサ というものがあります。
ということで今回は、C#のインデクサの使い方 について説明していきます。
  • インデクサとはなにか
  • インデクサの使い方
  • インデクサの使い方を知りたい方
  • インスタンスに配列のようにアクセスしたい方
  • コードの可読性を向上させたい方
こうちゃ
それでは、やっていきましょう!

インデクサとは

インデクサは インスタンスを配列のように扱うための仕組み です。
例えば通常は、以下のようにメソッドで値を取得します。
var a = image.GetPixel(x, y);
インデクサを使うと、次のように書けます。
var a = image[x, y];
かなり直感的になりますね。

インデクサの書き方

基本構文

public 戻り値 this[型 引数]
{
    get { /* 取得処理 */ }
    set { /* 設定処理 */ }
}

ポイントは以下です:

  • this[...] という特殊な構文を使う
  • getter と setter で値の取得・設定を行う

画像クラス「Image」のコード例

class Image
{
    public int X { get; private set; }
    public int Y { get; private set; }

    private int[] data;

    public Image(int width, int height)
    {
        X = width;
        Y = height;
        data = new int[X * Y];
    }

    public int this[int x, int y]
    {
        get
        {
            if (x < 0 || x >= X || y < 0 || y >= Y)
                throw new ArgumentOutOfRangeException("Pixel coordinates are out of bounds.");
            return data[y * X + x];
        }
        set
        {
            if (x < 0 || x >= X || y < 0 || y >= Y)
                throw new ArgumentOutOfRangeException("Pixel coordinates are out of bounds.");
            data[y * X + x] = value;
        }
    }
}
演算子のオーバーロード の記事では「SetPixel」「GetPixel」というメソッドで値の取得・設定を行っていました。
今回は、インデクサを使用して値を取得・設定するコードになっています。

インデクサの使い方

実際に使ってみましょう。
namespace Main
{
    class Program
    {
        static void Main(string[] args)
        {
            Image image = new Image(2, 2);

            for (int y = 0; y < 2; y++)
            {
                for (int x = 0; x < 2; x++)
                {
                    // インデクサ使用前
                    // image.SetPixel(x, y, x + y);     // image: [[0, 1], [1, 2]]

                    // インデクサ使用後
                    image[x, y] = x + y;     // image: [[0, 1], [1, 2]]
                }
            }
 
            string resultStr = "[";
            for (int y = 0; y < 2; y++)
            {
                resultStr += "[";
                for (int x = 0; x < 2; x++)
                {
                    // インデクサ使用前
                    // resultStr += image.GetPixel(x, y);

                    // インデクサ使用後
                    resultStr += image[x, y];
                    resultStr += (x == 0) ? ", " : "";
                }
                resultStr += "]";
                resultStr += (y == 0) ? ", " : "";
            }
            resultStr += "]";
            Console.WriteLine(resultStr);
        }
    }
}
インデクサ使用前後で以下のようにコードが変化しています。
// インデクサ使用前
// image.SetPixel(x, y, x + y);     // image: [[0, 1], [1, 2]]

// インデクサ使用後
image[x, y] = x + y;     // image: [[0, 1], [1, 2]]
// インデクサ使用前
// resultStr += image.GetPixel(x, y);

// インデクサ使用後
resultStr += image[x, y];
インデクサ使用後の方が、直感的でわかりやすいですね。
インデクサを使用して、しっかりと値の取得・設定ができているかも確認しましょう。
上記のコードを実際に実行してみます。
[[0, 1], [1, 2]]
このような結果になりました。
ちゃんと値の取得・設定もできていますね。

インデクサのメリット

可読性が向上する

これまでも書いてきましたが、インデクサを使用するメリットとしては、可読性の向上が大きいです。

image.GetPixel(x, y);

よりも

image[x, y];

の方が直感的で読みやすいですよね。

さらには

image3[x, y] = image1[x, y] + image2[x, y]

なんていう書き方もできてしまいます。
これはもう、配列を扱っているのと変わらないくらいわかりやすいですね!

自由度が高い

今回は例として、2次元配列のようなアクセスの仕方をしました。
ですがもちろん、1次元配列のようにすることも可能です。
public int this[int index] { get; set; }

はたまた、Dictionaryのような形式にすることも可能です。

public string this[int key] { get; set; }

このように、クラスの内容によって、さまざまな形でインデクサを使用することができます。

インデクサの注意点

インデクサも非常に便利だということがわかっていただけたかと思います。
ですがもちろん、注意点もあります。
それは

複雑な内容にしないようにする

ということです。
インデクサの基本的な役割は、インスタンス自体をデータ本体のように見せる ことです。
データが隠蔽されていても、さもデータに直接アクセスしているように見せられるから使いやすくなります。
もしインデクサで複雑な処理をしてしまったら、見た目と内容のギャップが出てしまう可能性があります。
例えば、今回の「Image」クラスでインデクサに複雑で重い処理が入っていたとします。
その状態で「Image」クラスの中身を知らない (公開APIしか把握していない) 人がインデクサを使用すると、期待した動作ができない場合があることは想像に難くありません。
また、インデクサを多用し、想定していたよりも処理に時間がかかってしまうなんてことも起こるかもしれません。
そのため、インデクサでは
  • シンプルな取得・設定に留める
  • 副作用を持つ可能性があるような処理を入れない
ことが重要です。

まとめ

インデクサの要点は

  • 配列のようにアクセスできる仕組み
  • this[...] を使って定義する
  • 可読性が向上し、直感的なコードになる
  • 複雑な処理を入れすぎないように注意する

となります。

さいごに

今回は、インデクサについて説明しました。
演算子のオーバーロードと組み合わせると、「自然に扱えるクラス」が作れるようになります。
ぜひ活用してみてくださいね!
今回はここまで♪
こうちゃ
それではみなさま、お疲れ様でした!
楽しいプログラミングライフを!
最新情報をチェックしよう!