C#の構造体(struct)とクラスの違いを徹底解説!初心者でも分かるC#オブジェクトの基本
生徒
「C#でstruct(ストラクト)とclass(クラス)ってどう違うんですか?」
先生
「いい質問ですね!C#では、struct(構造体)とclass(クラス)は見た目は似ていますが、動き方が大きく違います。」
生徒
「えっ、どういうことですか?同じように使えると思っていました…」
先生
「それでは、初心者でも分かるように、structとclassの違いを詳しく解説していきましょう!」
1. struct(構造体)とは?
C#のstruct(構造体)は、一言でいうと「値型(バリュータイプ)」に分類されるデータ構造です。初心者の方には少し難しい言葉かもしれませんが、普段使っているint(整数)やbool(真偽値)と同じ仲間だと考えると分かりやすいでしょう。
最大の特徴は、メモリ上にデータそのものが直接保存されるという点です。そのため、変数から別の変数へ代入(コピー)を行うと、中身がまるごと複製されます。これは「オリジナルのコピーを作成して渡す」ような動きになるため、コピー先で値を書き換えても、コピー元の値には一切影響を与えません。
それでは、プログラミングが初めての方でもイメージしやすいように、身近な「試験の点数」を管理する簡単な構造体のサンプルを見てみましょう。
// テストの結果を保持する「構造体」の定義
struct TestScore
{
public int Math; // 数学の点数
public int English; // 英語の点数
}
// 実際に構造体を使ってみる
TestScore score1 = new TestScore();
score1.Math = 80;
score1.English = 70;
// score1をscore2にコピー(まるごと複製される)
TestScore score2 = score1;
score2.Math = 100; // score2の数学だけ100点に書き換え
// それぞれの結果を表示
Console.WriteLine("score1の数学: " + score1.Math);
Console.WriteLine("score2の数学: " + score2.Math);
実行結果:
score1の数学: 80
score2の数学: 100
このように、score2の値を変更してもscore1は80点のまま変わりません。これが「値型」である構造体の振る舞いです。
構造体は、今回のような「点数」や「座標(X, Y)」、「RGBの色情報」といった、少数のデータをセットで扱う小さな箱のような役割に非常に適しています。軽量で無駄が少ないため、大量のデータを高速に処理したい場面でその真価を発揮します。
2. class(クラス)とは?
一方で、C#のclass(クラス)は「参照型(リファレンスタイプ)」と呼ばれます。これは構造体とは根本的にデータの持ち方が異なります。変数の中に直接データを入れるのではなく、データの「実体」が置かれているメモリ上の住所(参照先)だけを記録する仕組みです。
身近な例でいうと、構造体が「手紙そのもの」を渡すのに対し、クラスは「情報の場所が書かれた地図」を渡すようなイメージです。そのため、クラスを別の変数にコピーしても、コピーされるのは「場所の情報」だけであり、指し示している実体は同じものになります。片方の変数を通じて中身を書き換えると、もう片方の変数から見た内容も変わってしまうのが大きな特徴です。
プログラミングが初めての方でも理解しやすいように、名前を管理する「名札」のようなクラスの動作例を見てみましょう。
// 氏名を管理する「クラス」の定義
class StudentProfile
{
public string Name; // 生徒の名前
}
// 実際にクラスを使ってみる
StudentProfile student1 = new StudentProfile();
student1.Name = "田中さん";
// student1をstudent2に代入(データの場所が共有される)
StudentProfile student2 = student1;
student2.Name = "佐藤さん"; // student2の名前を書き換える
// それぞれの結果を表示
Console.WriteLine("student1の名前: " + student1.Name);
Console.WriteLine("student2の名前: " + student2.Name);
実行結果:
student1の名前: 佐藤さん
student2の名前: 佐藤さん
実行結果を見ると、student2の名前を変えただけなのに、student1の名前まで「佐藤さん」に変わっています。これは、両方の変数がメモリ上の全く同じデータを指し示しているためです。
クラスは、このように複数の場所で同じ情報を共有したり、複雑な処理(メソッド)を組み込んだりするのに非常に適しています。C#のオブジェクト指向プログラミングにおいて、最も中心的な役割を果たすのがこのクラスです。
3. 値型と参照型の違いとは?スタック領域とヒープ領域のイメージ
structとclassの最大の違いは、メモリ上でのデータの管理方法にあります。これを専門用語で「値型(Value Type)」と「参照型(Reference Type)」と呼びますが、初心者の方には少しイメージしづらいですよね。
そこで、日常の「書類のやり取り」に例えて、その決定的な違いを紐解いてみましょう。
「書類のコピーを渡す」
相手に書類を渡す際、コピー機で全く同じものを刷って渡します。相手がその紙に落書きをしても、手元にあるあなたのオリジナルは綺麗なままです。データの実体が別々に存在します。
「GoogleドキュメントのURLを教える」
相手に共有URL(参照)を教えます。相手がURLを開いて内容を書き換えると、あなたが見ているドキュメントの内容も変わってしまいます。実体は1つだけで、みんなでそれを見ている状態です。
この違いを理解するために、身近な「お財布(Wallet)」を例にしたプログラムで比較してみましょう。
// 値型(構造体)の場合:お財布そのものが複製される
struct ValueWallet
{
public int Money;
}
// 参照型(クラス)の場合:お財布の場所だけが共有される
class ReferenceWallet
{
public int Money;
}
// --- メインの処理 ---
// 1. 値型(構造体)の挙動
ValueWallet myValueWallet = new ValueWallet();
myValueWallet.Money = 1000;
ValueWallet friendsValueWallet = myValueWallet; // コピーを渡す
friendsValueWallet.Money = 5000; // 友達が自分の財布を書き換える
// 2. 参照型(クラス)の挙動
ReferenceWallet myRefWallet = new ReferenceWallet();
myRefWallet.Money = 1000;
ReferenceWallet friendsRefWallet = myRefWallet; // 同じ財布の「場所」を教える
friendsRefWallet.Money = 5000; // 友達が共有の財布を書き換える
Console.WriteLine("値型(struct)の自分の所持金: " + myValueWallet.Money);
Console.WriteLine("参照型(class)の自分の所持金: " + myRefWallet.Money);
実行結果:
値型(struct)の自分の所持金: 1000
参照型(class)の自分の所持金: 5000
この結果が示す通り、値型(struct)は代入した瞬間に中身が独立するため、相手の操作が自分に影響しません。一方、参照型(class)は同じ実体を指しているため、誰かが中身を変えれば全員に影響が及びます。
パフォーマンス面では、構造体はメモリの「スタック領域」という高速な場所に置かれ、使い終わるとすぐに片付けられます。クラスは「ヒープ領域」という広い場所に置かれ、ガベージコレクション(GC)という仕組みで管理されます。この特性の違いが、大規模な開発やゲーム制作における使い分けの決め手となるのです。
4. structとclassの使い分けのポイント
C#のプログラミングにおいて、構造体(struct)とクラス(class)のどちらを選ぶべきかは、そのデータがどのような性質を持つかによって決まります。闇雲に使い分けるのではなく、「データのサイズ」と「データの役割」に注目するのがSEOの観点からも推奨されるベストプラクティスです。
迷った時の判断基準(早見表)
- struct(構造体)を使うべきケース:
- データサイズが小さく(一般的に16バイト以下)、内容が単純なとき。
- 座標(X, Y)や色(R, G, B)のように、値そのものが重要で、コピーした際に独立していてほしいとき。
- class(クラス)を使うべきケース:
- 顧客情報や商品データなど、データ量が多く複雑なとき。
- 複数の場所で同じデータを参照し、変更を共有したいとき。
- 「継承」などのオブジェクト指向の機能を使って、柔軟に拡張したいとき。
例えば、ゲームのキャラクターの「現在位置」を考えてみましょう。位置情報は頻繁に計算され、一時的なデータとして扱われることが多いため、構造体が適しています。
// 位置を表す「構造体」:小さくて使い捨てやすい
struct Point
{
public int X;
public int Y;
}
// プレイヤーを表す「クラス」:複雑で状態を保持する
class Player
{
public string Name;
public Point Position;
}
// 1. 小さな座標データを作る
Point startPos = new Point();
startPos.X = 10;
startPos.Y = 20;
// 2. プレイヤーという大きな実体を作る
Player p1 = new Player();
p1.Name = "勇者";
p1.Position = startPos;
このように、「点(Point)」のような小さな部品は構造体で作り、その部品を組み合わせて作られる「プレイヤー(Player)」のような大きな実体はクラスで作るのが、メモリを効率よく使い、プログラムの動作を安定させる秘訣です。初心者のうちは、まずはクラスを使い、パフォーマンスが重要な小さなデータ群を作るときに構造体を検討するという流れで覚えるとスムーズです。
5. 実際の違いをコードで見てみよう
これまでの解説で、構造体(struct)は「中身のコピー」、クラス(class)は「場所の共有」というイメージをお伝えしました。ここでは、その違いがプログラムの動作にどう現れるのか、より具体的なサンプルコードで確認してみましょう。
プログラミング未経験の方でも分かりやすいよう、「データの入った箱を別の変数に移したとき、元の箱はどうなるか?」という視点で注目してみてください。
構造体(struct)の例:独立したコピーが作成される
構造体の場合、変数 a を b に代入した時点で、a の中身がそのまま複製されて b に入ります。そのため、b をいくら操作しても a には影響しません。
struct MyStruct
{
public int Value;
}
// 1. 最初の構造体を作成
MyStruct a = new MyStruct();
a.Value = 10;
// 2. aをbに代入(ここでデータが「複製」される)
MyStruct b = a;
b.Value = 20; // bの値を書き換える
// 3. それぞれの結果を確認
Console.WriteLine("aの値: " + a.Value);
Console.WriteLine("bの値: " + b.Value);
実行結果:
aの値: 10
bの値: 20
解説: b の値を20に変えても、a は10のままです。これは a と b がメモリ上の別々の場所に存在しているためです。
クラス(class)の例:同じデータを共有する
クラスの場合、変数 a を b に代入しても、中身はコピーされません。代わりに「データが置いてある場所(住所)」だけが共有されます。そのため、b を通じて中身を書き換えると、同じ場所を見ている a の値も変わってしまいます。
class MyClass
{
public int Value;
}
// 1. 最初のクラスを作成
MyClass a = new MyClass();
a.Value = 10;
// 2. aをbに代入(ここで「場所の情報」だけが共有される)
MyClass b = a;
b.Value = 20; // bを通じて共有データを書き換える
// 3. それぞれの結果を確認
Console.WriteLine("aの値: " + a.Value);
Console.WriteLine("bの値: " + b.Value);
実行結果:
aの値: 20
bの値: 20
解説: b を書き換えただけなのに、a の値まで20に変わりました。これは a と b が、メモリ上にある「たった一つの同じ実体」を指し示しているからです。この挙動の違いを理解することが、C#でバグの少ないプログラムを書くための第一歩となります。
6. その他の違いもチェックしよう
structとclassには、ほかにも次のような違いがあります。
- 継承(けいしょう):
classは他のクラスを継承できますが、structはできません。 - デフォルトコンストラクタ:
structには自分でコンストラクタを定義することはできますが、引数なしのコンストラクタは自動で作られ、変更できません。 - null(ヌル)代入:
classの変数にはnullを代入できますが、structにはできません(ただしNullable<T>型を使えば可能)。
7. 実務でよく使われるのはどっち?
実際の開発では、クラス(class)が使われることが多いです。なぜなら、アプリケーションの多くは複雑なデータを扱うため、柔軟に機能を追加できるクラスが適しているからです。
一方、構造体(struct)は、軽量で処理が早いため、ゲーム開発やグラフィック処理など、パフォーマンスが求められる場面で使われることが多いです。
まとめ
C#のstruct(構造体)とclass(クラス)は、初心者が最初につまずきやすいテーマでありながら、プログラムの設計やデータ管理を考えるうえで非常に重要な概念です。ここまでの記事で学んだように、structは値型であり、classは参照型という根本的な違いがあります。値型はデータそのものをコピーしますが、参照型はデータの位置を共有するため、同じインスタンスを触っているように動作します。この特徴を理解することで、メモリの扱い方、パフォーマンス、データの独立性などをしっかり意識したプログラムが書けるようになります。
特に、初心者が誤解しやすいのが「コピーしたら別物なのか、同じ物なのか」という点です。小さくて軽いデータを扱いたいときは構造体が役立ち、複雑で大きな処理をまとめたいときにはクラスが適しています。実務で多く使われるのはクラスですが、構造体を適切に活用することで、無駄なメモリ使用を避けたり、高速な処理を実現したりすることが可能になります。
また、structには継承ができない、classはnullを扱えるなど、それぞれ特性にも違いがあります。こういった特性を知らずに使うと不具合の原因になりやすいため、今回の内容を一度整理して、自分のアプリケーションでどちらを選ぶべきか判断できるようにすることが大切です。structとclassは見た目が似ていても目的が異なるため、役割を意識した設計が求められます。
ここで、構造体とクラスの違いを明確に理解するために、記事で学んだ内容を応用したサンプルコードをひとつ紹介します。小規模データを扱うstructと、複雑な情報を扱うclassを同時に利用することで、それぞれの特徴と用途がよりはっきりと見えてきます。
サンプルプログラム:構造体とクラスの特徴を活かしたデータ管理
下記のプログラムでは、「座標」をstructで管理し、「キャラクター情報」をclassで扱っています。ゲーム開発や業務アプリなど、さまざまな場面でこの設計はよく使われます。
struct Position
{
public int X;
public int Y;
public Position(int x, int y)
{
this.X = x;
this.Y = y;
}
}
class Character
{
public string Name;
public Position Pos;
public Character(string name, Position pos)
{
this.Name = name;
this.Pos = pos;
}
public void Move(int x, int y)
{
this.Pos.X += x;
this.Pos.Y += y;
}
public void ShowInfo()
{
Console.WriteLine("キャラクター名: " + this.Name);
Console.WriteLine("現在位置: X=" + this.Pos.X + ", Y=" + this.Pos.Y);
}
}
class Program
{
static void Main()
{
Position start = new Position(0, 0);
Character hero = new Character("勇者", start);
hero.Move(5, 3);
hero.ShowInfo();
}
}
この例では、座標のように小さく独立したデータはstructで扱い、キャラクターのように複数の情報をまとめたい場合にはclassを使うことで、データの扱いが整理され、動作の意図も分かりやすくなります。構造体はコピーされることで独立した値として扱われるため、元の値に影響を与えません。一方、クラスは参照型であるため、どこからでも同じキャラクター情報を共有できます。これが、用途に応じた適切なデータの選び方につながります。
structとclassの正しい使い分けを理解することで、コードの読みやすさや保守性が劇的に向上します。また、開発中の不具合を未然に防ぎやすくなるため、初心者だけでなく経験者にとっても重要な基礎知識です。値型と参照型の仕組みを理解したうえで、適切な場面で使い分けることが今後のC#プログラミングの上達につながります。
生徒
「structとclassって名前が似ているから同じように考えていましたが、値型と参照型でこんなに違うんですね…!」
先生
「そうなんです。特にコピーしたときの挙動が大きく違うので、どちらを使うべきか考えることが大切ですよ。」
生徒
「たしかに、structは軽くて速いけど、classは複雑なデータをまとめられるって理解できました!」
先生
「用途によって選べるようになると、プログラムの設計が一気に楽になりますよ。今回のサンプルも良い例でしたね。」
生徒
「次からは“値型か参照型か”を意識して、structとclassをちゃんと使い分けてみます!」