Day 48 のゴールと全体像
Day 48 のテーマは struct と record、そしてクラスとの違いです。
キーワードはこの3つです。
structrecord- クラスとの違い
ここまで「クラス」を中心にオブジェクト指向を学んできましたが、 C# には「クラス以外の型」も存在します。 今日はそれらを、実際の使いどころに焦点を当てて見ていきます。
クラスとの違いをざっくりイメージする
まずは「参照型」と「値型」という違い
C# では、
- クラス(
class) → 参照型(reference type) - 構造体(
struct) → 値型(value type)
という大きな違いがあります。
ざっくり言うと、
- クラス:「オブジェクトへの参照」を扱う
- struct:「中身そのもの」を扱う
というイメージです。
record は少し特殊で、
record class(参照型)record struct(値型)
の両方がありますが、 入門ではまず 「データを表すのに便利なクラスの仲間」として捉えるとよいです。
struct(構造体)とは何か
「小さな値のまとまり」を表すのに向いた型
struct は、
「いくつかの値をひとまとめにしたいけれど、 オブジェクトというほど大げさではない」
という場面に向いた型です。
たとえば、座標やサイズなどの「小さなデータのまとまり」によく使われます。
シンプルな struct の例:Point
// 2次元の座標を表す構造体
struct Point
{
public int X; // X座標
public int Y; // Y座標
public Point(int x, int y)
{
X = x;
Y = y;
}
// 座標を表示するメソッド
public void Show()
{
Console.WriteLine($"({X}, {Y})");
}
}
C#使う側はこうなります。
class Program
{
static void Main(string[] args)
{
// 構造体のインスタンスを作成
Point p1 = new Point(10, 20);
p1.Show(); // (10, 20)
Console.ReadLine();
}
}
C#ここまではクラスとあまり変わらないように見えますが、 値型であることが大きな違いを生みます。
struct の「値型らしさ」を体験する
コピーされるのは「参照」ではなく「中身」
クラスの場合、
class Person
{
public string Name;
}
C#Person a = new Person { Name = "Alice" };
Person b = a; // 参照をコピー
b.Name = "Bob";
Console.WriteLine(a.Name); // Bob(同じオブジェクトを指している)
C#というふうに、 「参照」をコピーしているだけなので、 中身は共有されます。
一方、struct の場合はこうです。
struct Point
{
public int X;
public int Y;
}
C#Point p1 = new Point { X = 10, Y = 20 };
Point p2 = p1; // 中身がコピーされる
p2.X = 99;
Console.WriteLine(p1.X); // 10(p1 と p2 は別物)
Console.WriteLine(p2.X); // 99
C#重要ポイント:
- struct は「値型」なので、代入すると 中身がコピーされる
- クラスは「参照型」なので、代入すると 参照がコピーされる
この違いが、 「どんな場面で struct を使うべきか」に関わってきます。
struct を使うのに向いている場面
1. 小さくて、頻繁にコピーされるデータ
- 座標(
Point) - サイズ(
Size) - 色(
Color)
など、 「小さな値のまとまり」で、 コピーされることが多いものは struct に向いています。
2. 「同じオブジェクトを共有する」より「値そのもの」を扱いたいとき
- 「この関数に渡すときは、値をコピーして渡したい」
- 「別の変数に代入しても、元の値は変わってほしくない」
という場面では、 struct の「値型らしさ」が役立ちます。
record とは何か
「データを表すクラス」を簡潔に書くための仕組み
record は、
「データを表すクラスを、 できるだけ少ないコードで、 しかも比較や表示もしやすく書きたい」
というニーズに応えるための機能です。
特に、
- 「プロパティだけを持つシンプルなデータクラス」
- 「値の等価性(中身が同じかどうか)を簡単に扱いたい」
といった場面で活躍します。
シンプルな record の例:User
// ユーザー情報を表すレコードクラス
public record User(string Name, int Age);
C#たったこれだけで、
NameとAgeのプロパティを持つ- コンストラクタが自動で定義される
ToString()がいい感じに実装されるEquals/GetHashCodeが「中身の値」で比較するように実装される
という、 「データクラスとして欲しい機能」が一気に手に入ります。
record の使い方を見てみる
インスタンスを作ってみる
class Program
{
static void Main(string[] args)
{
User u1 = new User("山田", 30);
User u2 = new User("山田", 30);
User u3 = new User("佐藤", 25);
Console.WriteLine(u1); // User { Name = 山田, Age = 30 }
// 中身が同じかどうかの比較
Console.WriteLine(u1 == u2); // true
Console.WriteLine(u1 == u3); // false
Console.ReadLine();
}
}
C#ここで注目してほしいのは、
Console.WriteLine(u1);の出力が、 「プロパティ名と値」を含んだ分かりやすい文字列になっていることu1 == u2が 「参照が同じか」ではなく「中身が同じか」で比較されていること
です。
通常のクラスだと、
class UserClass
{
public string Name { get; set; }
public int Age { get; set; }
}
C#UserClass c1 = new UserClass { Name = "山田", Age = 30 };
UserClass c2 = new UserClass { Name = "山田", Age = 30 };
Console.WriteLine(c1 == c2); // false(参照が違うので)
C#となり、 「中身が同じかどうか」を自分で実装しないといけません。
record は、 そのあたりを 「データクラスとしての便利機能」としてまとめて提供してくれるイメージです。
record とクラスの違いを整理する
クラス(通常の class)
- 参照型
==は「参照が同じかどうか」を比較する(デフォルト)ToString()はクラス名だけになることが多い- データクラスとして使う場合、
- コンストラクタ
- プロパティ
Equals/GetHashCodeToString()を自分で書くことが多い
record(record class)
- 参照型(
record classの場合) ==は「中身の値が同じかどうか」を比較するToString()は「プロパティ名と値」を含んだ分かりやすい文字列になる- データクラスとして使う場合に必要な機能が、 かなり自動で用意される
重要ポイント:
- record は「データを表すクラス」を簡潔に書くための仕組み
- クラスとの違いは「等価性の扱い」と「便利な自動実装」にある
struct・record・class の違いをまとめてイメージする
ざっくり3者比較
- class
- 参照型
- オブジェクト指向の中心
- 状態+振る舞いを持つ「普通のオブジェクト」
- struct
- 値型
- 小さな値のまとまりに向いている
- コピーすると中身が複製される
- record
- 「データを表すクラス」を簡潔に書くための仕組み
- 中身の値で等価性を比較する
- データクラスとしての便利機能が自動で付いてくる
入門段階では、
「普段は class を使う。 小さな値のまとまりなら struct。 データを表すだけなら record。」
くらいの感覚で捉えておくと、 実務でも迷いにくくなります。
struct・record の基本テンプレート
struct のテンプレート
// 2Dサイズを表す構造体
struct Size
{
public int Width;
public int Height;
public Size(int width, int height)
{
Width = width;
Height = height;
}
public void Show()
{
Console.WriteLine($"幅: {Width}, 高さ: {Height}");
}
}
C#record のテンプレート
// 商品情報を表すレコード
public record Product(string Id, string Name, decimal Price);
C#class Program
{
static void Main(string[] args)
{
// struct の利用
Size s1 = new Size(100, 200);
Size s2 = s1; // 中身がコピーされる
s2.Width = 300;
s1.Show(); // 幅: 100, 高さ: 200
s2.Show(); // 幅: 300, 高さ: 200
// record の利用
Product p1 = new Product("P001", "ノートPC", 120000m);
Product p2 = new Product("P001", "ノートPC", 120000m);
Console.WriteLine(p1); // Product { Id = P001, Name = ノートPC, Price = 120000 }
Console.WriteLine(p1 == p2); // true(中身が同じなので)
Console.ReadLine();
}
}
C#Day 48 のまとめ
Day 48 では、
- struct → 値型として「小さな値のまとまり」を表す型
- record → 「データを表すクラス」を簡潔に書き、 中身の値で等価性を扱えるようにする仕組み
- クラスとの違い → 参照型/値型、等価性の扱い、便利な自動実装の有無
を、座標・サイズ・ユーザー・商品などの例を通して学びました。
実務では、
- 「普通のオブジェクト」→ class
- 「小さな値のまとまり」→ struct
- 「データを表すだけの型」→ record
という使い分けができると、 コードの意図がぐっと伝わりやすくなります。
ぜひ、
- 自分で struct を1つ作って「値型らしさ(コピーのされ方)」を試してみる
- record で小さなデータクラスを作って、
==やToString()の挙動を確認してみる
という小さな練習を通して、 struct と record を 「ただのキーワード」ではなく、 設計を少しだけ気持ちよくしてくれる道具として、 少しずつ体に馴染ませていってください。
