Явные поля. Ключевое слово val

Ключевое слово val используется для объявления расположения для сохранения значения в типе класса или структуры без его инициализации. Расположения хранения, объявленные таким образом, называются явными полями. Ключевое слово val можно также использовать вместе с ключевым словом member для объявления автоматически реализуемого свойства. Дополнительные сведения о автоматически реализуемых свойствах см. в разделе Свойства.

Синтаксис

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

Remarks

Обычно поля в типе класса или структуры задаются с помощью привязки let. Однако привязки let должны инициализироваться как часть конструктора класса, что не всегда возможно, необходимо или желательно. Если требуется неинициализированное поле, можно использовать ключевое слово val.

Явные поля могут быть статическими или не статическими. Модификатор доступа может иметь значение public, privateили internal. По умолчанию явные поля являются открытыми. Это отличается от привязок let в классах, которые всегда являются закрытыми.

Атрибут DefaultValue является обязательным для явных полей в типах классов, имеющих основной конструктор. Этот атрибут указывает, что поле инициализируется нулевым значением. Тип поля должен поддерживать инициализацию нулем. Тип поддерживает инициализацию нулем, если он является одним из следующих типов.

  • Тип-примитив, который имеет нулевое значение.
  • Тип, поддерживающий значение null как нормальное значение, как аномальное значение или как представление значения. Сюда входят классы, кортежи, записи, функции, интерфейсы, ссылочные типы .NET, тип unit и типы размеченного объединения.
  • Тип значения .NET.
  • Структура, все поля которой поддерживают нулевое значение по умолчанию.

Например, неизменяемое поле с именем someField имеет резервное поле в скомпилированном в .NET представлении с именем someField@, и вы обращаетесь к хранимому значению, используя свойство с именем someField.

Для изменяемого поля скомпилированное в .NET представление является полем .NET.

Предупреждение

Пространство имен System.ComponentModel платформа .NET Framework содержит атрибут с тем же именем. Сведения об этом атрибуте см. в разделе DefaultValueAttribute.

В следующем коде показано использование явных полей и для сравнения привязки let в классе, имеющем первичный конструктор. Обратите внимание, что привязанное с помощью let поле myInt1 является закрытым. Если на привязанное с помощью let поле myInt1 существует ссылка из метода-члена, идентификатор самого поля this не требуется. Но если вы обращаетесь к явным полям myInt2 и myString, идентификатор самого поля обязателен.

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 появляются вместе в определении типа, то это определение автоматически реализуемого свойства. Дополнительные сведения см. в разделе Свойства.

См. также