C#: 列挙型と構造体

1. 列挙型 (enum)

列挙型(enum)は、名前付きの定数のセットを定義するために使用される値型であり、コードの可読性を高め、型安全性を確保します。

(1) 列挙型の定義

列挙型を定義するには、enum キーワードを使用します。デフォルトの基底型は int で、0 から始まり、順次番号が振られます。

▶ サンプル

CSHARP
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        Season current = Season.Autumn;
        System.Console.WriteLine(current);
        System.Console.WriteLine((int)current);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Autumn
2

(2) 基本型と明示的な代入

列挙型の基底型は、デフォルトで int になります。別の整数型を明示的に指定したり、メンバに特定の値を割り当てたりすることもできます。

▶ サンプル

CSHARP
enum Priority : byte
{
    Low = 1,
    Medium = 2,
    High = 3
}

enum HttpStatusCode
{
    OK = 200,
    NotFound = 404,
    InternalServerError = 500
}

class Program
{
    static void Main()
    {
        Priority p = Priority.High;
        System.Console.WriteLine((byte)p);
        System.Console.WriteLine((int)HttpStatusCode.NotFound);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
3
404

(3) 列挙型の変換

列挙型とその基底型は、互いに明示的に型変換することができます。

▶ サンプル

CSHARP
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        Season s = Season.Spring;
        int value = (int)s;
        System.Console.WriteLine(value);

        Season fromInt = (Season)2;
        System.Console.WriteLine(fromInt);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
0
Autumn

2. 列挙型クラスのメソッド

System.Enum クラスは、列挙型の操作を行うためのいくつかの静的メソッドを提供しています。

(1) 一般的な手法

方法 説明
Enum.Parse 文字列を列挙型に変換します。失敗した場合は例外をスローします
Enum.TryParse 文字列を列挙型に変換します。失敗した場合は false を返します
Enum.GetName 列挙型の値の名前を取得する
Enum.GetValues すべての列挙型値を取得する
Enum.IsDefined 値が列挙型で定義されているかどうかを判定します

▶ サンプル

CSHARP
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        Season parsed = (Season)Enum.Parse(typeof(Season), "Summer");
        System.Console.WriteLine(parsed);

        bool ok = Enum.TryParse<Season>("Winter", out Season result);
        System.Console.WriteLine($"{ok}, {result}");

        string name = Enum.GetName(typeof(Season), 2);
        System.Console.WriteLine(name);

        foreach (Season s in Enum.GetValues(typeof(Season)))
        {
            System.Console.WriteLine($"{s} = {(int)s}");
        }

        System.Console.WriteLine(Enum.IsDefined(typeof(Season), 1));
        System.Console.WriteLine(Enum.IsDefined(typeof(Season), 99));
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Summer
True, Winter
Autumn
Spring = 0
Summer = 1
Autumn = 2
Winter = 3
True
False

3. 構造体 (struct)

構造体(struct)は、少量のデータをカプセル化するのに適した値型です。構造体はスタック上に格納され、代入を行うと値全体がコピーされます。

(1) 構造体の定義

構造体を定義するには、struct キーワードを使用します。

▶ サンプル

CSHARP
struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public override string ToString()
    {
        return $"({X}, {Y})";
    }
}

class Program
{
    static void Main()
    {
        Point p1 = new Point(3, 4);
        Point p2 = p1;
        p2.X = 10;
        System.Console.WriteLine(p1);
        System.Console.WriteLine(p2);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
(3, 4)
(10, 4)

4. 構造体とクラス

構造体とクラスには、意味論や動作において重要な違いがあります。

(1) 比較

機能 構造体 クラス
型カテゴリ 値型 参照型
格納場所 スタック ヒープ
代入時の挙動 値をコピー 参照をコピー
継承 未対応(継承不可) 対応
インターフェース 実装可能 実装可能
デストラクタ なし あり
デフォルト値 すべてのフィールドがゼロ null

▶ サンプル

CSHARP
struct PointStruct
{
    public int X;
    public int Y;
}

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

class Program
{
    static void Main()
    {
        PointStruct s1 = new PointStruct();
        PointStruct s2 = s1;
        s2.X = 5;
        System.Console.WriteLine($"struct: s1.X={s1.X}, s2.X={s2.X}");

        PointClass c1 = new PointClass();
        PointClass c2 = c1;
        c2.X = 5;
        System.Console.WriteLine($"class: c1.X={c1.X}, c2.X={c2.X}");
    }
}
▶ 試してみよう
TEXT 📖 参照専用
struct: s1.X=0, s2.X=5
class: c1.X=5, c2.X=5

5. 構造体(Struct)を使うべき場面

構造体がクラスよりも常に優れているとは限りません。次のような場合には、構造体の使用を検討してください:

代表的な例:PointColorDateTimeTimeSpan

⚠️ 上記の条件が満たされない場合は、頻繁なコピーによるパフォーマンスの低下を防ぐために、クラスを使用してください。


6. readonly struct (C# 7.2+)

readonly struct は、すべてのフィールドが不変であることを保証します。コンパイラは、いかなる変更操作も阻止します。

▶ サンプル

CSHARP
readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public double DistanceTo(Point other)
    {
        int dx = X - other.X;
        int dy = Y - other.Y;
        return System.Math.Sqrt(dx * dx + dy * dy);
    }
}

class Program
{
    static void Main()
    {
        Point p1 = new Point(3, 4);
        Point p2 = new Point(0, 0);
        System.Console.WriteLine(p1.DistanceTo(p2));
    }
}
▶ 試してみよう
TEXT 📖 参照専用
5

7. record 構造体 (C# 10+)

record struct は、構造体(struct)の値セマンティクスと、レコードの値による等価性および簡潔な構文を組み合わせたもので、不変性や自動生成されたメンバを必要とするシナリオに適しています。

▶ サンプル

CSHARP
public class Point : IEquatable<Point>
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    public Point With(int? x = null, int? y = null)
    {
        return new Point(x ?? this.X, y ?? this.Y);
    }
    public bool Equals(Point other)
    {
        return other != null && X == other.X && Y == other.Y;
    }
    public override bool Equals(object obj) => Equals(obj as Point);
    public override int GetHashCode() => System.HashCode.Combine(X, Y);
    public override string ToString() => $"Point {{ X = {X}, Y = {Y} }}";
}

class Program
{
    static void Main()
    {
        Point p1 = new Point(1, 2);
        Point p2 = new Point(1, 2);
        System.Console.WriteLine(p1 == p2);
        System.Console.WriteLine(p1.With(x: 10));
    }
}
▶ 試してみよう
TEXT 📖 参照専用
True
Point { X = 10, Y = 2 }
💡 record struct は、EqualsToStringwith の式やその他のメンバーを自動生成し、定型コードを削減します。

❓ よくある質問

Q enum 型で整数型以外の型を指定することはできますか?
A いいえ、指定できるのは整数型のみです(byte、sbyte、short、ushort、int、uint、long、ulong)。
Q 構造体にはコンストラクタを定義できますか?
A はい、パラメータ付きコンストラクタを定義することはできますが、パラメータなしのコンストラクタはコンパイラによって自動的に生成されるため、独自に定義することはできません。
Q structはインターフェースを実装できますか?
A はい、structはインターフェースを実装できます。structがインターフェース型に変換される際(値から参照への変換)、ボクシングが発生します。
Q 読み取り専用構造体のフィールドで set を使用できますか?
A いいえ、読み取り専用構造体のすべてのフィールドは読み取り専用でなければならず、コンストラクタを通じてのみ初期化できます。
Q enum と const の違いは何ですか?
A enum は型安全性を備えた名前付き定数の集合であり、const は型をグループ化する機能を持たない単一のコンパイル時定数です。

📖 まとめ

📝 練習問題

  1. enum Direction { North, East, South, West } を定義し、すべての方向を順に処理して、その名称と整数値を出力するコードを記述する
  2. 幅と高さを指定して struct Rectangle を定義し、面積を計算するメソッドを用意し、代入によって値がコピーされることを確認する
  3. 前の演習で作成した Rectanglereadonly struct に変更し、すべてのフィールドが読み取り専用であり、コンストラクタを通じて初期化されるようにしてください
  4. Enum.TryParse および Enum.IsDefined を使用して、文字列を受け取り、対応する列挙型値またはデフォルト値を返す、安全な列挙型解析メソッドを作成してください。
  5. structclass をメソッドの引数として渡した際の動作の違い(値渡しと参照渡し)を比較するコードを作成してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%