明示的なフィールド: val キーワード

val キーワードを使用すると、クラス型または構造体型の値を格納する場所を初期化せずに宣言することができます。 この方法で宣言された保存場所は、"明示的なフィールド" と呼ばれます。 val キーワードの別の用途として、member キーワードと組み合わせて自動実装プロパティを宣言する方法があります。 自動実装プロパティの詳細については、「プロパティ」を参照してください。

構文

val [ mutable ] [ access-modifier ] field-name : type-name

解説

クラス型または構造体型のフィールドを定義するには、通常、let 束縛を使用します。 ただし、let 束縛は、クラス コンストラクターの一部として初期化する必要があります。これは、必ずしも可能または必要であるとは限らず、望ましくない場合もあります。 初期化されていないフィールドが必要な場合は、val キーワードを使用できます。

明示的なフィールドは静的にも非静的にもできます。 access-modifier は、publicprivate、または internal にできます。 既定では、明示的なフィールドは public です。 常に private であるクラスの let 束縛とは、この点が異なります。

プライマリ コンストラクターを持つクラス型の明示的なフィールドには、DefaultValue 属性が必要です。 この属性は、フィールドが 0 に初期化されることを示します。 フィールドの型ではゼロ初期化をサポートしている必要があります。 型が次のいずれかである場合は、ゼロ初期化がサポートされています。

  • 値が 0 のプリミティブ型。
  • 標準値、外れ値、または値の表現として null 値をサポートする型。 これには、クラス、タプル、レコード、関数、インターフェイス、.NET 参照型、unit 型、判別された共用体型が含まれます。
  • .NET 値型。
  • すべてのフィールドで既定値 0 がサポートされている構造体。

たとえば、someField と呼ばれる変更できないフィールドには、.NET によるコンパイル済み表現を使用した、someField@ という名前のバッキング フィールドが含まれており、ユーザーは someField という名前のプロパティを使用して、格納されている値にアクセスします。

変更可能なフィールドの場合、.NET によるコンパイル済み表現は .NET フィールドになります。

警告

.NET Framework の System.ComponentModel 名前空間には、同じ名前を持つ属性が含まれています。 この属性の詳細については、「DefaultValueAttribute」を参照してください。

次のコードは、明示的なフィールドの使用方法を示しています。また、比較のために、プライマリ コンストラクターを持つクラスの let 束縛も示しています。 let 束縛のフィールド myInt1 が private であることに注意してください。 let 束縛のフィールド myInt1 をメンバー メソッドから参照する際は、自己識別子 this は必要ありません。 ただし、明示的なフィールド myInt2myString を参照する際は、自己識別子が必要です。

type MyType() =
    let mutable myInt1 = 10
    [<DefaultValue>] val mutable myInt2 : int
    [<DefaultValue>] val mutable myString : string
    member this.SetValsAndPrint( i: int, str: string) =
       myInt1 <- i
       this.myInt2 <- i + 1
       this.myString <- str
       printfn "%d %d %s" myInt1 (this.myInt2) (this.myString)

let myObject = new MyType()
myObject.SetValsAndPrint(11, "abc")
// The following line is not allowed because let bindings are private.
// myObject.myInt1 <- 20
myObject.myInt2 <- 30
myObject.myString <- "def"

printfn "%d %s" (myObject.myInt2) (myObject.myString)

出力は次のようになります。

11 12 abc
30 def

次のコードは、プライマリ コンストラクターを持たないクラスでの明示的なフィールドの使用方法を示しています。 この場合、DefaultValue 属性は必要ありませんが、その型用に定義されているコンストラクターですべてのフィールドが初期化される必要があります。

type MyClass =
    val a : int
    val b : int
    // The following version of the constructor is an error
    // because b is not initialized.
    // new (a0, b0) = { a = a0; }
    // The following version is acceptable because all fields are initialized.
    new(a0, b0) = { a = a0; b = b0; }

let myClassObj = new MyClass(35, 22)
printfn "%d %d" (myClassObj.a) (myClassObj.b)

出力は 35 22 になります。

次のコードは、構造体での明示的なフィールドの使用方法を示しています。 構造体は値型であるため、フィールドの値を 0 に設定するパラメーターなしのコンストラクターが自動的に含まれます。 そのため、DefaultValue 属性は必要ありません。

type MyStruct =
    struct
        val mutable myInt : int
        val mutable myString : string
    end

let mutable myStructObj = new MyStruct()
myStructObj.myInt <- 11
myStructObj.myString <- "xyz"

printfn "%d %s" (myStructObj.myInt) (myStructObj.myString)

出力は 11 xyz になります。

mutable フィールドのある構造体を mutable キーワードを使用しないで初期化する場合、割り当ては割り当ての直後に破棄される構造体のコピーで機能することに注意してください。 そのため、構造体は変更されません。

[<Struct>]
type Foo =
    val mutable bar: string
    member self.ChangeBar bar = self.bar <- bar
    new (bar) = {bar = bar}

let foo = Foo "1"
foo.ChangeBar "2" //make implicit copy of Foo, changes the copy, discards the copy, foo remains unchanged
printfn "%s" foo.bar //prints 1

let mutable foo' = Foo "1"
foo'.ChangeBar "2" //changes foo'
printfn "%s" foo'.bar //prints 2

明示的なフィールドは日常的に使用するためのものではありません。 通常、可能な場合は、明示的なフィールドでなく、クラスで let 束縛を使用してください。 明示的なフィールドは、特定の相互運用のシナリオ (ネイティブ API に対するプラットフォーム呼び出しで使用される構造体を定義する必要がある場合など) や COM 相互運用のシナリオで役立ちます。 詳細については、「外部関数」を参照してください。 また、プライマリ コンストラクターを持たないクラスを生成する F# コード ジェネレーターを使用している場合にも、明示的なフィールドが必要になることがあります。 明示的なフィールドは、thread-static 変数や同様のコンストラクターでも役立ちます。 詳細については、「System.ThreadStaticAttribute」を参照してください。

キーワード member val が型定義にまとめて表示された場合は、自動的に実装されたプロパティの定義です。 詳細については、「 プロパティで定義されているインターフェイスのプライベート C++ 固有の実装です。

関連項目