クラスと構造体の使い分け
C#には、独自のデータ型を定義する方法として「クラス(class)」と「構造体(struct)」の2つがあります。
クラスが「参照型(Reference Type)」であり、柔軟な継承や多態性を持つリッチなオブジェクト指向のために使われるのに対し、構造体は「値型(Value Type)」です。構造体は、座標データや数値のペアなど、小さくて軽量なデータを扱うのに適しています。
この記事では、構造体の基本的な定義方法と、C# 7.2以降で推奨されるreadonly struct(不変構造体)について解説します。
構造体の基本構文
構造体の定義はクラスと非常によく似ていますが、classの代わりにstructキーワードを使用します。
[アクセス修飾子] [readonly] struct 構造体名
{
// フィールド、プロパティ、コンストラクタ、メソッドなどを定義
}
重要なルール:引数なしコンストラクタ
C# 10.0以前では、構造体に「引数なしのコンストラクタ」を定義することはできませんでした(C# 10.0以降は可能になりましたが、通常はフィールドがデフォルト値で初期化されることを前提とします)。
readonly struct(不変構造体)
構造体を使用する場合、パフォーマンス向上とバグ防止の観点から、作成後に値を変更できない「不変(Immutable)」な設計にすることが強く推奨されます。
structキーワードの前にreadonlyを付けることで、その構造体のすべてのフィールドとプロパティが読み取り専用であることを強制できます。
コード例:地理座標(GeoCoordinate)構造体
緯度(Latitude)と経度(Longitude)を持つ、読み取り専用の構造体を定義する例です。
using System;
public class StructExample
{
public static void Main()
{
// コンストラクタで値を設定してインスタンス化
var location = new GeoCoordinate(35.6895, 139.6917);
// ToString() メソッドの結果を表示
Console.WriteLine(location.ToString());
// 値は読み取り専用
// location.Latitude = 0; // コンパイルエラーになる
}
}
/// <summary>
/// 地理座標を表す読み取り専用構造体
/// </summary>
public readonly struct GeoCoordinate
{
// 1. プロパティ (読み取り専用)
// readonly struct なので、set アクセサーは定義できない (init は可)
public double Latitude { get; }
public double Longitude { get; }
// 2. コンストラクタ
// 構造体生成時に値を初期化する
public GeoCoordinate(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
// 3. メソッド
// Object クラスの ToString をオーバーライドして、わかりやすい文字列表現にする
public override string ToString()
{
return $"Lat: {Latitude:F4}, Lon: {Longitude:F4}";
}
}
出力結果:
Lat: 35.6895, Lon: 139.6917
クラスとの決定的な違い:値のコピー
構造体は「値型」であるため、変数への代入やメソッドへの引数渡しの際に、参照(場所)ではなく、データの中身そのものがコピーされます。
var loc1 = new GeoCoordinate(10, 20);
// 値そのものがコピーされる
var loc2 = loc1;
// もし loc2 を変更できたとしても、loc1 には影響しない
// (クラスの場合は参照がコピーされるため、loc1 も影響を受ける)
この「コピーされる」という特性があるため、巨大な構造体を作るとコピーのコストが高くなり、かえってパフォーマンスが低下します。構造体は、一般的に16バイト以下(int 4つ分程度)の小さなサイズに留めるのが目安とされています。
まとめ
構造体(struct)は、クラスに似ていますが、メモリ管理や代入の挙動が異なる「値型」です。
- 用途: 座標、複素数、RGB色など、小さくて単純なデータのまとまり。
- 推奨:
readonly structとして定義し、作成後に値が変わらない(不変)ように設計する。 - 注意: サイズが大きくなるとコピーコストがかかるため、大きなデータにはクラスを使用する。
C#では、基本的な型(int, double, DateTimeなど)もすべて構造体で定義されています。これらと同様の軽量な型を自作したい場合に、構造体は非常に有効な手段です。
