3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

構造体とインデクサ

Last updated at Posted at 2024-06-12

はじめに

classとstructの違いをきちんと意識せずに使ってたらエラーが出たよ、というだけの話です。常識と言えば常識な感じのネタなので、こんなの常識、と思う方はスルーしてください😅

  • こんなの何に使うの?
    使いたい時もあるんです。
  • 構造体はイミュータブルが常識では?
    それは知ってる。
  • ただの配列のほうがいいんじゃ?
    話せば長くなるので。

ことの発端

パフォーマンスが気になる用途で、構造体のList<>を使おうとした時でした。

// 再現例
List<Point> points = new List<Point>();
points.Add( new Point( 0, 0 ) );
points[ 0 ].X++;  // エラーCS1612

このコードは、コンパイルエラーになります。

エラーCS1612 変数ではないため、'List.this[int]' の戻り値を変更できません

何が起きてる?

簡単なコードで再現してみましょう。

// あくまで再現例
class Points {
    private Point[] points = new Point[10];

    public Point this[int index] {
        get {
            return points[index];
        }
        set {
            points[index] = value;
        }
    }
}

Points points = new Points();
points[0].X = 1;  // エラーCS1612

Pointは構造体(struct)なので、値型です。インデクサのgetは、int型2個分、合わせて8バイト分のメモリ領域を、points配列の中からコピーして直に戻してきます。
コピーしてきた戻り値を変更しても無駄なのは、お気づきの通り。それを指摘しているのが、この、エラーCS1612です。

さてどうする - その1

パフォーマンスを犠牲にして、新しいPointを作って差し替えます。これなら構造体もイミュータブルでいられます。素晴らしいね!

points[0] = new Point( points[0].X + 1, points[0].Y );

さてどうする - その2

パフォーマンスを犠牲にして、構造体をあきらめてクラスにします。クラスなら参照型なので、自由に変更できます。

class MyPoint {
    public int X;
    public int Y;
}

結局どうした?

パフォーマンスを犠牲にしたくなかったので、独自のListモドキを実装しました。その中で使ったのが参照戻り値というやつ。

// あくまで再現例
class Points {
    private Point[] points = new Point[10];

    public ref Point this[int index] {
        get {
            return ref points[index];
        }
    }
}

Points points = new Points();
points[0].X = 1;  // エラーにならない!
points[1] = new Point( 1, 1 );  // まるごともいけるよ!

こうすることで、getインデクサは、points配列の中の一つの要素の参照(ポインタ) を返すようになります。これによって、参照型の場合と同じように、メンバ変数を自由に変更できるようになります。
またこの場合、setインデクサは必要ありません。代入式の左辺に書かれた場合は、getインデクサが返す参照先のメモリ領域に、直にコピーされます。

今回はインデクサ絡みの問題でこの問題に気付いたのですが、構造体を返すプロパティでもよくあるケース、みたいですね。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?