構造体Structs

構造体は、データメンバーと関数メンバーを含むことができるデータ構造を表すという点でクラスに似ています。Structs are similar to classes in that they represent data structures that can contain data members and function members. ただし、クラスとは異なり、構造体は値型であり、ヒープ割り当ては必要ありません。However, unlike classes, structs are value types and do not require heap allocation. 構造体型の変数は、構造体のデータを直接格納します。一方、クラス型の変数には、データへの参照 (後者はオブジェクトと呼ばれます) が含まれます。A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to the data, the latter known as an object.

構造体は、値セマンティクスを持つ小規模なデータ構造に特に便利です。Structs are particularly useful for small data structures that have value semantics. 構造体の主な例としては、複素数、座標系のポイント、ディクショナリのキーと値のペアなどがあります。Complex numbers, points in a coordinate system, or key-value pairs in a dictionary are all good examples of structs. これらのデータ構造にとって重要なのは、データメンバーが少ないこと、継承または参照 id を使用する必要がないこと、および割り当てによって参照の代わりに値がコピーされる値のセマンティクスを使用して便宜的に実装できることです。Key to these data structures is that they have few data members, that they do not require use of inheritance or referential identity, and that they can be conveniently implemented using value semantics where assignment copies the value instead of the reference.

単純型」で説明されているようC#に、によって提供される単純型 (intdoubleboolなど) は、実際にはすべての構造体型です。As described in Simple types, the simple types provided by C#, such as int, double, and bool, are in fact all struct types. これらの定義済みの型が構造体であるのと同様に、構造体と演算子のオーバーロードを使用して、 C#新しい "プリミティブ" 型を言語で実装することもできます。Just as these predefined types are structs, it is also possible to use structs and operator overloading to implement new "primitive" types in the C# language. このような型の2つの例については、この章の最後 (構造体の例) を参照してください。Two examples of such types are given at the end of this chapter (Struct examples).

構造体の宣言Struct declarations

Struct_declarationは、新しい構造体を宣言するtype_declaration (型宣言) です。A struct_declaration is a type_declaration (Type declarations) that declares a new struct:

struct_declaration
    : attributes? struct_modifier* 'partial'? 'struct' identifier type_parameter_list?
      struct_interfaces? type_parameter_constraints_clause* struct_body ';'?
    ;

Struct_declarationは、省略可能な属性のセット (属性) の後に省略可能な一連のstruct_modifiers (struct 修飾子)、その後に省略可能な partial 修飾子、オプションの type_parameter_listの指定 (型パラメーター)、オプションのstruct_interfacesの指定 (部分修飾子) で構成さ struct れます。) の後に省略可能なtype_parameter_constraints_clauses 仕様 (型パラメーターの制約)、その後にstruct_body (構造体の本体)、必要に応じてセミコロンを続けて指定します。A struct_declaration consists of an optional set of attributes (Attributes), followed by an optional set of struct_modifiers (Struct modifiers), followed by an optional partial modifier, followed by the keyword struct and an identifier that names the struct, followed by an optional type_parameter_list specification (Type parameters), followed by an optional struct_interfaces specification (Partial modifier) ), followed by an optional type_parameter_constraints_clauses specification (Type parameter constraints), followed by a struct_body (Struct body), optionally followed by a semicolon.

Struct 修飾子Struct modifiers

Struct_declarationには、必要に応じて構造体の修飾子のシーケンスを含めることができます。A struct_declaration may optionally include a sequence of struct modifiers:

struct_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | struct_modifier_unsafe
    ;

同じ修飾子が struct 宣言で複数回出現する場合、コンパイル時エラーになります。It is a compile-time error for the same modifier to appear multiple times in a struct declaration.

構造体宣言の修飾子は、クラス宣言 (クラス宣言) と同じ意味を持ちます。The modifiers of a struct declaration have the same meaning as those of a class declaration (Class declarations).

Partial 修飾子Partial modifier

partial 修飾子は、このstruct_declarationが部分型の宣言であることを示します。The partial modifier indicates that this struct_declaration is a partial type declaration. 外側の名前空間または型宣言内で同じ名前を持つ複数の部分構造体宣言を組み合わせると、部分型で指定された規則に従って1つの構造体宣言が形成されます。Multiple partial struct declarations with the same name within an enclosing namespace or type declaration combine to form one struct declaration, following the rules specified in Partial types.

構造体のインターフェイスStruct interfaces

Struct 宣言にはstruct_interfaces指定を含めることができます。この場合、構造体は、指定されたインターフェイス型を直接実装すると言います。A struct declaration may include a struct_interfaces specification, in which case the struct is said to directly implement the given interface types.

struct_interfaces
    : ':' interface_type_list
    ;

インターフェイスの実装については、「インターフェイスの実装」で詳しく説明します。Interface implementations are discussed further in Interface implementations.

構造体の本体Struct body

構造体のstruct_bodyは、構造体のメンバーを定義します。The struct_body of a struct defines the members of the struct.

struct_body
    : '{' struct_member_declaration* '}'
    ;

構造体のメンバーStruct members

構造体のメンバーは、 struct_member_declarations によって導入されたメンバーと、型 System.ValueTypeから継承されたメンバーで構成されます。The members of a struct consist of the members introduced by its struct_member_declarations and the members inherited from the type System.ValueType.

struct_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | static_constructor_declaration
    | type_declaration
    | struct_member_declaration_unsafe
    ;

クラスと構造体の違いについては、反復子を通じてクラスメンバーに指定されたクラスメンバーの説明も、構造体のメンバーに適用されます。Except for the differences noted in Class and struct differences, the descriptions of class members provided in Class members through Iterators apply to struct members as well.

クラスと構造体の違いClass and struct differences

構造体は、いくつかの重要な点でクラスとは異なります。Structs differ from classes in several important ways:

  • 構造体は値型 (値のセマンティクス) です。Structs are value types (Value semantics).
  • すべての構造体型は、クラス System.ValueType (継承) を暗黙的に継承します。All struct types implicitly inherit from the class System.ValueType (Inheritance).
  • 構造体型の変数への代入では、代入される値のコピーが作成されます (代入)。Assignment to a variable of a struct type creates a copy of the value being assigned (Assignment).
  • 構造体の既定値は、すべての値の型フィールドを既定値に設定し、すべての参照型フィールドを null (既定値) に設定することによって生成される値です。The default value of a struct is the value produced by setting all value type fields to their default value and all reference type fields to null (Default values).
  • ボックス化およびボックス化解除の操作は、構造体型と object (ボックス化およびボックス化解除) の間の変換に使用されます。Boxing and unboxing operations are used to convert between a struct type and object (Boxing and unboxing).
  • this の意味は、構造体 (このアクセス) によって異なります。The meaning of this is different for structs (This access).
  • 構造体のインスタンスフィールド宣言には、変数初期化子 (フィールド初期化子) を含めることはできません。Instance field declarations for a struct are not permitted to include variable initializers (Field initializers).
  • 構造体は、パラメーターなしのインスタンスコンストラクター (コンストラクター) の宣言を許可されていません。A struct is not permitted to declare a parameterless instance constructor (Constructors).
  • 構造体はデストラクター (デストラクター) の宣言を許可されていません。A struct is not permitted to declare a destructor (Destructors).

値のセマンティクスValue semantics

構造体は値型 (値) であり、値のセマンティクスがあると言います。Structs are value types (Value types) and are said to have value semantics. 一方、クラスは参照型 (参照型) であり、参照セマンティクスがあると言います。Classes, on the other hand, are reference types (Reference types) and are said to have reference semantics.

構造体型の変数は、構造体のデータを直接格納します。一方、クラス型の変数には、データへの参照 (後者はオブジェクトと呼ばれます) が含まれます。A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to the data, the latter known as an object. A 型のインスタンスフィールドが構造体 B に含まれており、A が構造体型である場合、AB または Bから構築された型に依存している場合、コンパイル時エラーになります。When a struct B contains an instance field of type A and A is a struct type, it is a compile-time error for A to depend on B or a type constructed from B. 構造体 X は、XY型のインスタンスフィールドを含んでいる場合に、構造体 Yに直接依存します。A struct X directly depends on a struct Y if X contains an instance field of type Y. この定義により、構造体が依存する構造体の完全なセットは、リレーションシップに直接依存します。Given this definition, the complete set of structs upon which a struct depends is the transitive closure of the directly depends on relationship. 次に例を示します。For example

struct Node
{
    int data;
    Node next; // error, Node directly depends on itself
}

Node には、独自の型のインスタンスフィールドが含まれているため、エラーが発生します。is an error because Node contains an instance field of its own type. 別の例Another example

struct A { B b; }

struct B { C c; }

struct C { A a; }

AB、および C の各型が互いに依存しているため、エラーが発生します。is an error because each of the types A, B, and C depend on each other.

クラスを使用すると、2つの変数が同じオブジェクトを参照する可能性があるため、1つの変数に対する操作が、もう一方の変数によって参照されるオブジェクトに影響を与える可能性があります。With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. 構造体を使用すると、変数にはそれぞれ独自のデータコピーが含まれます (refout パラメーター変数の場合を除く)。一方の変数に対する操作が他方に影響を与えることはありません。With structs, the variables each have their own copy of the data (except in the case of ref and out parameter variables), and it is not possible for operations on one to affect the other. さらに、構造体は参照型ではないため、構造体型の値を nullすることはできません。Furthermore, because structs are not reference types, it is not possible for values of a struct type to be null.

宣言を指定します。Given the declaration

struct Point
{
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

コード片the code fragment

Point a = new Point(10, 10);
Point b = a;
a.x = 100;
System.Console.WriteLine(b.x);

10値を出力します。outputs the value 10. b への a の割り当てによって値のコピーが作成されるため、ba.xへの割り当ての影響を受けません。The assignment of a to b creates a copy of the value, and b is thus unaffected by the assignment to a.x. Point はクラスとして宣言されていましたが、ab は同じオブジェクトを参照するため、出力が 100 されます。Had Point instead been declared as a class, the output would be 100 because a and b would reference the same object.

継承Inheritance

すべての構造体型は、クラス System.ValueTypeから暗黙的に継承されます。このクラスはクラス objectから継承されます。All struct types implicitly inherit from the class System.ValueType, which, in turn, inherits from class object. 構造体の宣言では実装されたインターフェイスのリストを指定できますが、構造体の宣言で基底クラスを指定することはできません。A struct declaration may specify a list of implemented interfaces, but it is not possible for a struct declaration to specify a base class.

構造体型は抽象ではなく、常に暗黙的にシールされます。Struct types are never abstract and are always implicitly sealed. したがって、abstract および sealed の修飾子は、構造体の宣言では許可されません。The abstract and sealed modifiers are therefore not permitted in a struct declaration.

継承は構造体ではサポートされていないため、構造体メンバーの宣言されたアクセシビリティを protected または protected internalすることはできません。Since inheritance isn't supported for structs, the declared accessibility of a struct member cannot be protected or protected internal.

構造体の関数メンバーを abstract または virtualすることはできません。また、override 修飾子は、System.ValueTypeから継承されたメソッドをオーバーライドする場合にのみ許可されます。Function members in a struct cannot be abstract or virtual, and the override modifier is allowed only to override methods inherited from System.ValueType.

代入Assignment

構造体型の変数への代入では、割り当てられている値のコピーが作成されます。Assignment to a variable of a struct type creates a copy of the value being assigned. これは、参照によって識別されるオブジェクトではなく、参照をコピーするクラス型の変数への代入とは異なります。This differs from assignment to a variable of a class type, which copies the reference but not the object identified by the reference.

代入と同様に、構造体が値パラメーターとして渡される場合や、関数メンバーの結果として返される場合は、構造体のコピーが作成されます。Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. 構造体は、ref または out パラメーターを使用して、関数メンバーへの参照によって渡すことができます。A struct may be passed by reference to a function member using a ref or out parameter.

構造体のプロパティまたはインデクサーが割り当てのターゲットである場合、プロパティまたはインデクサーアクセスに関連付けられたインスタンス式は、変数として分類される必要があります。When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. インスタンス式が値として分類されている場合は、コンパイル時エラーが発生します。If the instance expression is classified as a value, a compile-time error occurs. 詳細については、「単純な割り当て」を参照してください。This is described in further detail in Simple assignment.

既定の値Default values

既定値」で説明されているように、いくつかの種類の変数は、作成時に自動的に既定値に初期化されます。As described in Default values, several kinds of variables are automatically initialized to their default value when they are created. クラス型およびその他の参照型の変数の場合、この既定値は nullです。For variables of class types and other reference types, this default value is null. ただし、構造体は nullできない値型であるため、構造体の既定値は、すべての値の型フィールドを既定値に設定し、すべての参照型フィールドを nullに設定することによって生成される値です。However, since structs are value types that cannot be null, the default value of a struct is the value produced by setting all value type fields to their default value and all reference type fields to null.

上で宣言された Point 構造体を参照しています。例Referring to the Point struct declared above, the example

Point[] a = new Point[100];

配列内の各 Point を、x フィールドと y フィールドを0に設定することによって生成される値に初期化します。initializes each Point in the array to the value produced by setting the x and y fields to zero.

構造体の既定値は、構造体の既定のコンストラクターによって返される値に対応します (既定のコンストラクター)。The default value of a struct corresponds to the value returned by the default constructor of the struct (Default constructors). クラスとは異なり、構造体では、パラメーターなしのインスタンスコンストラクターを宣言することはできません。Unlike a class, a struct is not permitted to declare a parameterless instance constructor. 代わりに、すべての構造体には、暗黙的にパラメーターなしのインスタンスコンストラクターがあります。これは、すべての値型フィールドを既定値に設定し、すべての参照型フィールドを nullに設定した結果の値を常に返します。Instead, every struct implicitly has a parameterless instance constructor which always returns the value that results from setting all value type fields to their default value and all reference type fields to null.

構造体は、既定の初期化状態が有効な状態であると見なすように設計する必要があります。Structs should be designed to consider the default initialization state a valid state. この例では、In the example

using System;

struct KeyValuePair
{
    string key;
    string value;

    public KeyValuePair(string key, string value) {
        if (key == null || value == null) throw new ArgumentException();
        this.key = key;
        this.value = value;
    }
}

ユーザー定義のインスタンスコンストラクターは、明示的に呼び出された場合にのみ null 値を保護します。the user-defined instance constructor protects against null values only where it is explicitly called. KeyValuePair 変数が既定値の初期化の対象となる場合、key フィールドと value フィールドは null になり、この状態を処理するために構造体を準備する必要があります。In cases where a KeyValuePair variable is subject to default value initialization, the key and value fields will be null, and the struct must be prepared to handle this state.

ボックス化とボックス化解除Boxing and unboxing

クラス型の値は object 型、またはコンパイル時に別の型として参照を扱うことによって、クラスによって実装されるインターフェイス型に変換できます。A value of a class type can be converted to type object or to an interface type that is implemented by the class simply by treating the reference as another type at compile-time. 同様に、object 型の値またはインターフェイス型の値は、参照を変更せずにクラス型に変換できます (この場合は、実行時の型チェックが必要です)。Likewise, a value of type object or a value of an interface type can be converted back to a class type without changing the reference (but of course a run-time type check is required in this case).

構造体は参照型ではないため、これらの操作は構造体型に対して異なる方法で実装されます。Since structs are not reference types, these operations are implemented differently for struct types. 構造体型の値が object 型または構造体によって実装されるインターフェイス型に変換されると、ボックス化操作が行われます。When a value of a struct type is converted to type object or to an interface type that is implemented by the struct, a boxing operation takes place. 同様に、型 object の値またはインターフェイス型の値が構造体型に変換されると、ボックス化解除操作が行われます。Likewise, when a value of type object or a value of an interface type is converted back to a struct type, an unboxing operation takes place. クラス型に対する同じ操作との主な違いは、ボックス化とボックス化解除によって、ボックス化されたインスタンスの内外に構造体の値がコピーされることです。A key difference from the same operations on class types is that boxing and unboxing copies the struct value either into or out of the boxed instance. したがって、ボックス化またはボックス化解除の後に、ボックス化解除された構造体に加えられた変更は、ボックス化された構造体に反映されません。Thus, following a boxing or unboxing operation, changes made to the unboxed struct are not reflected in the boxed struct.

System.Object (EqualsGetHashCodeToStringなど) から継承された仮想メソッドを構造体型がオーバーライドする場合、構造体型のインスタンスを介して仮想メソッドを呼び出すと、ボックス化が発生しません。When a struct type overrides a virtual method inherited from System.Object (such as Equals, GetHashCode, or ToString), invocation of the virtual method through an instance of the struct type does not cause boxing to occur. これは、構造体が型パラメーターとして使用されていて、型パラメーター型のインスタンスを通じて呼び出しが行われる場合にも当てはまります。This is true even when the struct is used as a type parameter and the invocation occurs through an instance of the type parameter type. 例 :For example:

using System;

struct Counter
{
    int value;

    public override string ToString() {
        value++;
        return value.ToString();
    }
}

class Program
{
    static void Test<T>() where T: new() {
        T x = new T();
        Console.WriteLine(x.ToString());
        Console.WriteLine(x.ToString());
        Console.WriteLine(x.ToString());
    }

    static void Main() {
        Test<Counter>();
    }
}

プログラムの出力は次のようになります。The output of the program is:

1
2
3

副作用を持つ ToString には不適切なスタイルですが、この例では、x.ToString()の3つの呼び出しに対してボックス化が行われていないことを示しています。Although it is bad style for ToString to have side effects, the example demonstrates that no boxing occurred for the three invocations of x.ToString().

同様に、制約された型パラメーターのメンバーにアクセスするときに、ボックス化が暗黙的に行われることはありません。Similarly, boxing never implicitly occurs when accessing a member on a constrained type parameter. たとえば、インターフェイス ICounter に、値の変更に使用できるメソッド Increment が含まれているとします。For example, suppose an interface ICounter contains a method Increment which can be used to modify a value. ICounter が制約として使用されている場合、Increment メソッドの実装は Increment が呼び出された変数への参照を使用して呼び出され、ボックス化されたコピーは呼び出されません。If ICounter is used as a constraint, the implementation of the Increment method is called with a reference to the variable that Increment was called on, never a boxed copy.

using System;

interface ICounter
{
    void Increment();
}

struct Counter: ICounter
{
    int value;

    public override string ToString() {
        return value.ToString();
    }

    void ICounter.Increment() {
        value++;
    }
}

class Program
{
    static void Test<T>() where T: ICounter, new() {
        T x = new T();
        Console.WriteLine(x);
        x.Increment();                    // Modify x
        Console.WriteLine(x);
        ((ICounter)x).Increment();        // Modify boxed copy of x
        Console.WriteLine(x);
    }

    static void Main() {
        Test<Counter>();
    }
}

Increment の最初の呼び出しでは x変数の値が変更されます。The first call to Increment modifies the value in the variable x. これは、xのボックス化されたコピーの値を変更する Incrementへの2回目の呼び出しとは同じではありません。This is not equivalent to the second call to Increment, which modifies the value in a boxed copy of x. このため、プログラムの出力は次のようになります。Thus, the output of the program is:

0
1
1

ボックス化とボックス化解除の詳細については、「ボックス化とボックス化解除」を参照してください。For further details on boxing and unboxing, see Boxing and unboxing.

意味Meaning of this

クラスのインスタンスコンストラクターまたはインスタンス関数メンバー内では、this は値として分類されます。Within an instance constructor or instance function member of a class, this is classified as a value. このため、this を使用して、関数メンバーが呼び出されたインスタンスを参照することはできますが、クラスの関数メンバーの this に割り当てることはできません。Thus, while this can be used to refer to the instance for which the function member was invoked, it is not possible to assign to this in a function member of a class.

構造体のインスタンスコンストラクター内では、this は構造体型の out パラメーターに対応し、構造体のインスタンス関数メンバー内では、this 構造体型の ref パラメーターに対応します。Within an instance constructor of a struct, this corresponds to an out parameter of the struct type, and within an instance function member of a struct, this corresponds to a ref parameter of the struct type. どちらの場合も、this は変数として分類され、this に割り当てるか ref または out パラメーターとして渡すことによって、関数メンバーが呼び出された構造体全体を変更することができます。In both cases, this is classified as a variable, and it is possible to modify the entire struct for which the function member was invoked by assigning to this or by passing this as a ref or out parameter.

フィールド初期化子Field initializers

既定値」で説明されているように、構造体の既定値は、すべての値の型フィールドを既定値に設定し、すべての参照型フィールドを nullすることによって生成される値で構成されます。As described in Default values, the default value of a struct consists of the value that results from setting all value type fields to their default value and all reference type fields to null. このため、構造体では、インスタンスフィールドの宣言に変数初期化子を含めることはできません。For this reason, a struct does not permit instance field declarations to include variable initializers. この制限は、インスタンスフィールドにのみ適用されます。This restriction applies only to instance fields. 構造体の静的フィールドには、変数初期化子を含めることができます。Static fields of a struct are permitted to include variable initializers.

The example

struct Point
{
    public int x = 1;  // Error, initializer not permitted
    public int y = 1;  // Error, initializer not permitted
}

インスタンスフィールドの宣言に変数初期化子が含まれているため、エラーが発生しています。is in error because the instance field declarations include variable initializers.

コンストラクターConstructors

クラスとは異なり、構造体では、パラメーターなしのインスタンスコンストラクターを宣言することはできません。Unlike a class, a struct is not permitted to declare a parameterless instance constructor. 代わりに、すべての構造体には、暗黙的にパラメーターなしのインスタンスコンストラクターがあります。このコンストラクターは、すべての値型フィールドを既定値に設定し、すべての参照型フィールドを null (既定のコンストラクター) に設定した結果の値を常に返します。Instead, every struct implicitly has a parameterless instance constructor which always returns the value that results from setting all value type fields to their default value and all reference type fields to null (Default constructors). 構造体は、パラメーターを持つインスタンスコンストラクターを宣言できます。A struct can declare instance constructors having parameters. 次に例を示します。For example

struct Point
{
    int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

上記の宣言を指定した場合、ステートメントはGiven the above declaration, the statements

Point p1 = new Point();
Point p2 = new Point(0, 0);

どちらも xPoint を作成し、0に初期化 y ます。both create a Point with x and y initialized to zero.

構造体インスタンスコンストラクターには、base(...)フォームのコンストラクター初期化子を含めることはできません。A struct instance constructor is not permitted to include a constructor initializer of the form base(...).

Struct インスタンスコンストラクターがコンストラクター初期化子を指定していない場合、this 変数は構造体型の out パラメーターに対応し、out パラメーターと同様に、コンストラクターが返されるすべての場所で this を確実に代入 (明確な代入) する必要があります。If the struct instance constructor doesn't specify a constructor initializer, the this variable corresponds to an out parameter of the struct type, and similar to an out parameter, this must be definitely assigned (Definite assignment) at every location where the constructor returns. Struct インスタンスコンストラクターでコンストラクター初期化子が指定されている場合、this 変数は、構造体型の ref パラメーターに対応し、ref パラメーターと同様に、this はコンストラクター本体のエントリで確実に割り当てられていると見なされます。If the struct instance constructor specifies a constructor initializer, the this variable corresponds to a ref parameter of the struct type, and similar to a ref parameter, this is considered definitely assigned on entry to the constructor body. 次に示すインスタンスコンストラクターの実装について考えてみましょう。Consider the instance constructor implementation below:

struct Point
{
    int x, y;

    public int X {
        set { x = value; }
    }

    public int Y {
        set { y = value; }
    }

    public Point(int x, int y) {
        X = x;        // error, this is not yet definitely assigned
        Y = y;        // error, this is not yet definitely assigned
    }
}

構築されている構造体のすべてのフィールドが確実に代入されるまで、インスタンスメンバー関数 (プロパティ X および Yの set アクセサーを含む) を呼び出すことはできません。No instance member function (including the set accessors for the properties X and Y) can be called until all fields of the struct being constructed have been definitely assigned. 唯一の例外には、自動的に実装されるプロパティ (自動的に実装されるプロパティ) が含まれます。The only exception involves automatically implemented properties (Automatically implemented properties). 明確な代入規則 (単純な代入式) は、その構造体型のインスタンスコンストラクター内の構造体型の自動プロパティへの割り当てを明示的に除外します。このような代入は、自動プロパティの隠しバッキングフィールドの明確な代入と見なされます。The definite assignment rules (Simple assignment expressions) specifically exempt assignment to an auto-property of a struct type within an instance constructor of that struct type: such an assignment is considered a definite assignment of the hidden backing field of the auto-property. したがって、次のものを使用できます。Thus, the following is allowed:

struct Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y) {
        X = x;      // allowed, definitely assigns backing field
        Y = y;      // allowed, definitely assigns backing field
    }

デストラクターDestructors

構造体は、デストラクターの宣言を許可されていません。A struct is not permitted to declare a destructor.

静的コンストラクターStatic constructors

構造体の静的コンストラクターは、クラスの場合と同じ規則のほとんどに従います。Static constructors for structs follow most of the same rules as for classes. 構造体型の静的コンストラクターの実行は、アプリケーションドメイン内で発生する次のイベントの最初のイベントによってトリガーされます。The execution of a static constructor for a struct type is triggered by the first of the following events to occur within an application domain:

  • 構造体型の静的メンバーが参照されています。A static member of the struct type is referenced.
  • 構造体型の明示的に宣言されたコンストラクターが呼び出されます。An explicitly declared constructor of the struct type is called.

構造体型の既定値 (既定値) を作成しても、静的コンストラクターはトリガーされません。The creation of default values (Default values) of struct types does not trigger the static constructor. (例として、配列内の要素の初期値が挙げられます)。(An example of this is the initial value of elements in an array.)

構造体の例Struct examples

次に、struct 型を使用して、定義済みの言語の型と同様に使用できる型を作成する2つの大きな例を示しますが、セマンティクスは変更されています。The following shows two significant examples of using struct types to create types that can be used similarly to the predefined types of the language, but with modified semantics.

データベースの整数型Database integer type

次の DBInt 構造体は、int 型の値の完全なセットと、不明な値を示す追加の状態を表すことができる整数型を実装します。The DBInt struct below implements an integer type that can represent the complete set of values of the int type, plus an additional state that indicates an unknown value. これらの特性を持つ型は、データベースで一般的に使用されます。A type with these characteristics is commonly used in databases.

using System;

public struct DBInt
{
    // The Null member represents an unknown DBInt value.

    public static readonly DBInt Null = new DBInt();

    // When the defined field is true, this DBInt represents a known value
    // which is stored in the value field. When the defined field is false,
    // this DBInt represents an unknown value, and the value field is 0.

    int value;
    bool defined;

    // Private instance constructor. Creates a DBInt with a known value.

    DBInt(int value) {
        this.value = value;
        this.defined = true;
    }

    // The IsNull property is true if this DBInt represents an unknown value.

    public bool IsNull { get { return !defined; } }

    // The Value property is the known value of this DBInt, or 0 if this
    // DBInt represents an unknown value.

    public int Value { get { return value; } }

    // Implicit conversion from int to DBInt.

    public static implicit operator DBInt(int x) {
        return new DBInt(x);
    }

    // Explicit conversion from DBInt to int. Throws an exception if the
    // given DBInt represents an unknown value.

    public static explicit operator int(DBInt x) {
        if (!x.defined) throw new InvalidOperationException();
        return x.value;
    }

    public static DBInt operator +(DBInt x) {
        return x;
    }

    public static DBInt operator -(DBInt x) {
        return x.defined ? -x.value : Null;
    }

    public static DBInt operator +(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value + y.value: Null;
    }

    public static DBInt operator -(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value - y.value: Null;
    }

    public static DBInt operator *(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value * y.value: Null;
    }

    public static DBInt operator /(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value / y.value: Null;
    }

    public static DBInt operator %(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value % y.value: Null;
    }

    public static DBBool operator ==(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value == y.value: DBBool.Null;
    }

    public static DBBool operator !=(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value != y.value: DBBool.Null;
    }

    public static DBBool operator >(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value > y.value: DBBool.Null;
    }

    public static DBBool operator <(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value < y.value: DBBool.Null;
    }

    public static DBBool operator >=(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value >= y.value: DBBool.Null;
    }

    public static DBBool operator <=(DBInt x, DBInt y) {
        return x.defined && y.defined? x.value <= y.value: DBBool.Null;
    }

    public override bool Equals(object obj) {
        if (!(obj is DBInt)) return false;
        DBInt x = (DBInt)obj;
        return value == x.value && defined == x.defined;
    }

    public override int GetHashCode() {
        return value;
    }

    public override string ToString() {
        return defined? value.ToString(): "DBInt.Null";
    }
}

データベースのブール型Database boolean type

次の DBBool 構造体は、3つの値を持つ論理型を実装します。The DBBool struct below implements a three-valued logical type. この型に指定できる値は、DBBool.TrueDBBool.False、および DBBool.Nullです。この場合、Null メンバーは不明な値を示します。The possible values of this type are DBBool.True, DBBool.False, and DBBool.Null, where the Null member indicates an unknown value. このような3つの値の論理型は、データベースで一般的に使用されます。Such three-valued logical types are commonly used in databases.

using System;

public struct DBBool
{
    // The three possible DBBool values.

    public static readonly DBBool Null = new DBBool(0);
    public static readonly DBBool False = new DBBool(-1);
    public static readonly DBBool True = new DBBool(1);

    // Private field that stores -1, 0, 1 for False, Null, True.

    sbyte value;

    // Private instance constructor. The value parameter must be -1, 0, or 1.

    DBBool(int value) {
        this.value = (sbyte)value;
    }

    // Properties to examine the value of a DBBool. Return true if this
    // DBBool has the given value, false otherwise.

    public bool IsNull { get { return value == 0; } }

    public bool IsFalse { get { return value < 0; } }

    public bool IsTrue { get { return value > 0; } }

    // Implicit conversion from bool to DBBool. Maps true to DBBool.True and
    // false to DBBool.False.

    public static implicit operator DBBool(bool x) {
        return x? True: False;
    }

    // Explicit conversion from DBBool to bool. Throws an exception if the
    // given DBBool is Null, otherwise returns true or false.

    public static explicit operator bool(DBBool x) {
        if (x.value == 0) throw new InvalidOperationException();
        return x.value > 0;
    }

    // Equality operator. Returns Null if either operand is Null, otherwise
    // returns True or False.

    public static DBBool operator ==(DBBool x, DBBool y) {
        if (x.value == 0 || y.value == 0) return Null;
        return x.value == y.value? True: False;
    }

    // Inequality operator. Returns Null if either operand is Null, otherwise
    // returns True or False.

    public static DBBool operator !=(DBBool x, DBBool y) {
        if (x.value == 0 || y.value == 0) return Null;
        return x.value != y.value? True: False;
    }

    // Logical negation operator. Returns True if the operand is False, Null
    // if the operand is Null, or False if the operand is True.

    public static DBBool operator !(DBBool x) {
        return new DBBool(-x.value);
    }

    // Logical AND operator. Returns False if either operand is False,
    // otherwise Null if either operand is Null, otherwise True.

    public static DBBool operator &(DBBool x, DBBool y) {
        return new DBBool(x.value < y.value? x.value: y.value);
    }

    // Logical OR operator. Returns True if either operand is True, otherwise
    // Null if either operand is Null, otherwise False.

    public static DBBool operator |(DBBool x, DBBool y) {
        return new DBBool(x.value > y.value? x.value: y.value);
    }

    // Definitely true operator. Returns true if the operand is True, false
    // otherwise.

    public static bool operator true(DBBool x) {
        return x.value > 0;
    }

    // Definitely false operator. Returns true if the operand is False, false
    // otherwise.

    public static bool operator false(DBBool x) {
        return x.value < 0;
    }

    public override bool Equals(object obj) {
        if (!(obj is DBBool)) return false;
        return value == ((DBBool)obj).value;
    }

    public override int GetHashCode() {
        return value;
    }

    public override string ToString() {
        if (value > 0) return "DBBool.True";
        if (value < 0) return "DBBool.False";
        return "DBBool.Null";
    }
}