構造体型 (C# リファレンス)

"構造体型" (または "構造体型") とは、データおよび関連する機能をカプセル化できる 値の型です。 構造体型を定義するには、struct キーワードを使用します。

public struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }

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

構造体型には、"値のセマンティクス" があります。 つまり、構造体型の変数には、型のインスタンスが含まれます。 既定では、変数値が代入時にコピーされ、引数がメソッドに渡され、メソッドの結果が返されます。 構造体型の変数の場合は、型のインスタンスがコピーされます。 詳細については、値の型に関するページを参照してください。

通常は、構造体型を使用して、ほとんどまたはまったく動作を提供しない小さなデータ中心型を設計します。 たとえば、.NET では、構造体型を使用して数値 (整数実数の両方)、ブール値Unicode 文字時刻インスタンスが表現されます。 型の動作に重点を置いている場合は、class を定義することを検討してください。 クラス型には "参照セマンティクス" があります。 つまり、クラス型の変数には、インスタンス自体ではなく、型のインスタンスへの参照が含まれています。

構造体型には値セマンティクスがあるため、"変更不可" の構造体型を定義することをお勧めします。

readonly 構造体

C# 7.2 以降では、readonly 修飾子を使用して、構造体型が変更不可であることを宣言します。 readonly 構造体のすべてのデータ メンバーを、次のように読み取り専用にする必要があります。

  • すべてのフィールド宣言には、readonly 修飾子が必要です
  • 自動的に実装されるものも含めて、すべてのプロパティは、読み取り専用である必要があります。 C# 9.0 以降では、プロパティに init アクセサーが含まれる場合があります。

それにより、readonly 構造体のどのメンバーも構造体の状態を変更しないことが保証されます。 C# 8.0 以降では、コンストラクターを除く他のインスタンス メンバーは、暗黙的に readonly になるということです。

注意

readonly 構造体でも、変更可能な参照型のデータ メンバーは、それ自身の状態を変更できます。 たとえば、List<T> インスタンスを置き換えることはできませんが、新しい要素をそれに追加することはできます。

次のコードでは、C# 9.0 以降で使用できる、init 専用プロパティの setter を持つ readonly 構造体を定義します。

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

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

readonly インスタンス メンバー

C# 8.0 以降では、readonly 修飾子を使用して、インスタンス メンバーで構造体の状態を変更しないことを宣言することもできます。 構造体の型全体を readonly として宣言できない場合は、readonly 修飾子を使用して、構造体の状態を変更しないインスタンス メンバーをマークします。

readonly インスタンス メンバー内では、構造体のインスタンス フィールドに割り当てることはできません。 ただし、readonly メンバーから非 readonly メンバーを呼び出すことができます。 その場合、コンパイラを使用して構造体インスタンスのコピーを作成し、そのコピーで非 readonly メンバーを呼び出します。 その結果、元の構造インスタンスは変更されません。

通常、readonly 修飾子を次の種類のインスタンス メンバーに適用します。

  • メソッド:

    public readonly double Sum()
    {
        return X + Y;
    }
    

    System.Object で宣言されたメソッドをオーバーライドするメソッドに readonly 修飾子を適用することもできます。

    public readonly override string ToString() => $"({X}, {Y})";
    
  • プロパティとインデクサー:

    private int counter;
    public int Counter
    {
        readonly get => counter;
        set => counter = value;
    }
    

    プロパティまたはインデクサーの両方のアクセサーに readonly 修飾子を適用する必要がある場合は、プロパティまたはインデクサーの宣言でそれを適用します。

    注意

    プロパティの宣言に readonly 修飾子が存在するかどうかに関係なく、コンパイラによって自動実装プロパティget アクセサーが readonly として宣言されます。

    C# 9.0 以降では、init アクセサーを持つプロパティまたはインデクサーに readonly 修飾子を適用することができます。

    public readonly double X { get; init; }
    

readonly 修飾子を構造体型の静的メンバーに適用することはできません。

パフォーマンスの最適化のためにコンパイラで readonly 修飾子を使用する場合があります。 詳細については、「安全で効率的な C# コードを記述する」をご覧ください。

構造体型の設計に関する制限事項

構造体型を設計する場合は、class 型と同じ機能を使用できますが、次の例外があります。

  • パラメーターなしのコンストラクターを宣言することはできません。 すべての構造体型には、型の既定値を生成する暗黙的なパラメーターなしのコンストラクターが既に備わっています。

  • インスタンス フィールドまたはプロパティを、それらの宣言で初期化することはできません。 ただし、static または const フィールド、あるいは静的プロパティについては、それらの宣言で初期化することができます。

  • 構造体型のコンストラクターでは、型のすべてのインスタンス フィールドを初期化する必要があります。

  • 構造体型は、他のクラスまたは構造体型から継承することができないほか、クラスのベースとすることもできません。 ただし、構造体型では interfaces を実装することができます。

  • 構造体型内でファイナライザーを宣言することはできません。

構造体型のインスタンス化

C# では、宣言された変数を使用するには、事前にこれを初期化する必要があります。 構造体型の変数は (null 許容値型の変数でない限り) null とすることができないため、対応する型のインスタンスをインスタンス化する必要があります。 それにはいくつかの方法があります。

通常は、new 演算子を使用して適切なコンストラクターを呼び出すことによって、構造体型をインスタンス化します。 すべての構造体型に、少なくとも 1 つのコンストラクターがあります。 それは暗黙的なパラメーターなしのコンストラクターであり、型の既定値を生成するものです。 また、既定の値式を使用して、型の既定値を生成することもできます。

構造体型のすべてのインスタンス フィールドにアクセスできる場合は、それを new 演算子なしでインスタンス化することもできます。 その場合は、インスタンスを初めて使用する前に、すべてのインスタンス フィールドを初期化する必要があります。 その方法を次の例に示します。

public static class StructWithoutNew
{
    public struct Coords
    {
        public double x;
        public double y;
    }

    public static void Main()
    {
        Coords p;
        p.x = 3;
        p.y = 4;
        Console.WriteLine($"({p.x}, {p.y})");  // output: (3, 4)
    }
}

組み込みの値型の場合は、対応するリテラルを使用して型の値を指定します。

構造体型の変数を参照渡しする

構造体型の変数を引数としてメソッドに渡す場合、またはメソッドから構造体型の値を返す場合は、構造体型のインスタンス全体がコピーされます。 これは、大規模な構造体型を必要とするハイパフォーマンスのシナリオの場合、ご利用のコードのパフォーマンスに影響を与える可能性があります。 値のコピーを回避するには、構造体型の変数を参照渡しします。 引数を参照渡しする必要があることを示すには、refout、または in のメソッド パラメーター修飾子を使用します。 メソッドの結果を参照渡しによって返すには、ref 戻り値を使用します。 詳細については、「安全で効率的な C# コードを記述する」をご覧ください。

ref 構造体

C# 7.2 以降、ref 修飾子は、構造体型の宣言内で使用できます。 ref 構造体型のインスタンスはスタック上に割り当てられます。マネージド ヒープにエスケープすることはできません。 これを確実にするために、コンパイラでは次のように ref 構造体型の使用が制限されます。

  • ref 構造体を配列の要素型にすることはできません。
  • ref 構造体をクラスまたは非 ref 構造体のフィールドの宣言型にすることはできません。
  • ref 構造体ではインターフェイスを実装できません。
  • ref 構造体を System.ValueType または System.Object にボックス化することはできません。
  • ref 構造体を型引数にすることはできません。
  • ref 構造体変数をラムダ式またはローカル関数でキャプチャすることはできません。
  • ref 構造体変数を async メソッド内で使用することはできません。 ただし、Task または Task<TResult> を返す場合など、同期メソッドで ref 構造体変数を使用することはできます。
  • ref 構造体変数を反復子内で使用することはできません。

通常、ref 構造体型のデータ メンバーも含む型が必要な場合は、ref 構造体型を定義します。

public ref struct CustomRef
{
    public bool IsValid;
    public Span<int> Inputs;
    public Span<int> Outputs;
}

ref 構造体を readonly として宣言するには、型宣言内で readonly 修飾子と ref 修飾子を組み合わせます (readonly 修飾子は ref 修飾子よりも前にある必要があります)。

public readonly ref struct ConversionRequest
{
    public ConversionRequest(double rate, ReadOnlySpan<double> values)
    {
        Rate = rate;
        Values = values;
    }

    public double Rate { get; }
    public ReadOnlySpan<double> Values { get; }
}

.NET では、ref 構造体の例として System.Span<T>System.ReadOnlySpan<T> があります。

struct 制約

また、struct 制約struct キーワードを使用して、型パラメーターが null 非許容値型であることを指定します。 構造体と列挙型の型は、どちらも struct 制約を満たしています。

変換

どの構造体型にも (ref 構造体型を除く)、System.ValueType 型と System.Object 型の間にボックス化およびボックス化解除の変換が存在します。 また、構造体型と、これによって実装されるインターフェイスとの間にも、ボックス化とボックス化解除の変換が存在します。

C# 言語仕様

詳細については、C# 言語仕様の「構造体」セクションを参照してください。

C# 7.2 以降で導入された機能の詳細については、次の機能の提案に関するメモを参照してください。

関連項目