显式字段:val 关键字

val 关键字用于声明在类或结构类型中存储一个值(但不初始化该值)的位置。 以这种方式声明的存储位置被称为“显式字段”。 关键字 val 的另一个用法与 member 关键字结合使用来声明自动实现的属性。 有关自动实现的属性的详细信息,请参阅 “属性”。

语法

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

备注

在类或结构类型中定义字段的通常方式是使用 let 绑定。 但是,let 绑定必须初始化为类构造函数的一部分,而这并不总是可能、有必要或必需实现的。 当你需要一个未初始化的字段时,可以使用 val 关键。

显式字段可以为静态或非静态的。 access-modifier 可以是 publicprivateinternal。 默认情况下,显式字段是公共的。 这不同于类中的 let 绑定,后者始终是私有的。

具有主构造函数的类类型中的显式字段上需要 DefaultValue 特性。 此特性指定该字段被初始化为零。 字段的类型必须支持零初始化。 如果一个类型为以下类型之一,则该类型支持零初始化:

  • 具有零值的基元类型。
  • 一种支持 Null 值作为标准值、异常值或值表示形式的类型。 这包括类、元组、记录、函数、接口、.NET 引用类型、unit 类型以及可区分联合类型。
  • 一个 .NET 值类型。
  • 一种结构,其字段均支持默认值零。

例如,称为 someField 的不可变字段具有一个 .NET 编译表示形式的支持字段,该字段名为 someField@,你可以使用名为 someField 的属性访问存储值。

对于可变字段,.NET 编译的表示形式是一个 .NET 字段。

警告

.NET Framework 命名空间 System.ComponentModel 包含具有相同名称的特性。 有关该属性的信息,请参见 DefaultValueAttribute

以下代码展示了显式字段的用法,作为对比,还展示了具有主构造函数的类中的 let 绑定。 注意:与 let 字段绑定的 myInt1 是私有的。 当从成员方法引用与 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

以下代码展示了结构中显式字段的用法。 由于结构是值类型,它自动具有无参数构造函数,该函数将其字段的值设置为零。 因此,不需要 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# 代码生成器时,该生成器会发出不具有主构造函数的类。 显式字段对于线程静态变量或类似的构造而言也十分有用。 有关详细信息,请参阅 System.ThreadStaticAttribute

当关键字 member val 在一个类型定义中同时出现,它就是一个自动实现属性的定义。 有关详细信息,请参阅 属性

请参阅