C#で開発していると、「構造体(struct
)」という型と、「読み取り専用構造体(readonly struct
)」という表記を目にすることがあります。
「読み取り専用ってだけでしょ?」「パフォーマンスが良くなるって本当?」
そんな疑問を持つ方のために、今回はまずstructとは何かから始めて、readonly struct
が安全性とパフォーマンスの両面でどれだけ重要かを、丁寧に解説していきます。
structの基本
そもそも struct とは?
C#には2つの基本的なデータ型があります。
参照型(class)と値型(struct)です。
class
は、実体はメモリの別の場所(ヒープ)に置かれ、変数にはその場所を指し示す参照(ポインタのようなもの)が入ります。
// class(参照型)
public class Person
{
public string Name;
}
var personA = new Person { Name = "Alice" };
var personB = personA; // 参照がコピーされる
personB.Name = "Bob";
Console.WriteLine(personA.Name); // → Bob(personBを変えたのにpersonAも変わる)
ここで重要なのは、personB = personA
のような代入では、中身そのものはコピーされておらず、参照先の「アドレス(ポインタ)」だけが複製されたという点です。
だからどちらの変数からも同じPersonインスタンスを操作してしまうことになります。
struct
は、変数自体が実体そのものを持ちます。他の変数に代入すると、中身が複製されます(=実体のコピー)。
// struct(値型)
public struct Point
{
public int X;
public int Y;
}
var pointA = new Point { X = 1, Y = 2 };
var pointB = pointA; // 実体がコピーされる
pointB.X = 100;
Console.WriteLine(pointA.X); // → 1(pointAとpointBは別物)
Point
のような構造体(値型)は、代入のたびに中身が完全にコピーされる(別物が複製される)ため、後から一方を変更してももう一方には影響がありません。
🧱 structは「軽くて独立した値」を表すのに適している
C#では、短命で軽量なデータ(たとえば座標、色、サイズなど)を表現したいときに struct
を使います。
ただし、値がコピーされる(=複製される)性質を持つため、うっかり書き換えをすると「えっ、変わってない?」というトラブルに繋がることもあります。
そこで登場するのが、readonly struct
です。
readonly structとは?
readonly struct
は、その構造体が絶対に書き換わらないことをC#コンパイラに保証させるための宣言です。
たとえばこんなコード。
public readonly struct Position
{
public int X { get; }
public int Y { get; }
public Position(int x, int y)
{
X = x;
Y = y;
}
}
このように readonly
をつけると、
- フィールドやプロパティがすべて読み取り専用でなければならない
- 外部からの書き換えも、内部からの書き換えも不可能になる
つまり「イミュータブル(不変)な値オブジェクト」として設計できるわけです。
⚠️ readonly structの値を変えようとすると?
以下のように、読み取り専用のフィールドを変更しようとするとエラーになります。
var p = new Position(1, 2);
p.X = 10; // コンパイルエラー
これは readonly
によって「この構造体は絶対に変更してはならない」と明示されているためです。
一度生成したら中身を変えない。
これが readonly struct
の基本思想です。
パフォーマンスにも効果あり!
readonly struct
は単なる「書き換え禁止マーク」ではありません。
C#のコンパイラやJIT(実行時コンパイラ)に「これは安全だからコピーを減らしていいよ」と伝える働きもあります。
通常の struct
はメソッド内でフィールドにアクセスしたりプロパティを使うだけで、勝手にコピー(複製)が発生してしまうことがあります。
これは、構造体がメモリの別の場所にあるかもしれないとき、安全のためにコピーしてから使う設計になっているためです。
しかし readonly struct
なら「どうせ絶対に中身は変わらない」と確定しているので、不要なコピー処理が最適化によって省かれる可能性が高まります。
つまり、速くて軽いコードが書けるようになるというわけです。
書き換えられるstructは避けるべき?
これはC#界隈では「ほぼYES」だそうです。
public struct Counter
{
public int Count;
public void Increment()
{
Count++;
}
}
このような「書き換える構造体」は、たとえば List<Counter>
などに入れて操作すると、コピーに対して操作してしまい、リスト中のデータが変わっていないということが簡単に起きます。この使い方は完全に禁忌です。
C#において、構造体は「読み取り専用」である前提の設計が基本。
「値を変えたいならclassにすべき」とよく言われるのはこのためです。
まとめ:readonly structは明確な意図とメリットを持った設計
readonly struct
は「変更禁止」+「パフォーマンス改善」のダブル効果を持つ- 構造体は値型。代入や引数渡しで中身がコピー(複製)されるため、意図せぬ変更が発生しやすい
readonly
にすることで、不変性が保証され、安全性も速度もアップ- 「変更が必要」なら、それは
class
を使うべき設計のサイン
特にUnityなどのゲーム開発では、パフォーマンスや明快なデータ構造が求められる場面が多いため、
「迷ったらreadonly struct」→「変更したいならclass」という指針は非常に有効です。
Unity/C# をもっと体系的に学ぶには?
【現役メンターに質問しながら短期集中】
👉️ Unityコースで学んでみる