Właściwości (F#)

Właściwości to elementy członkowskie reprezentujące wartości skojarzone z obiektem.

Składnia

// Property that has both get and set defined.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with [accessibility-modifier] get() =
    get-function-body
and [accessibility-modifier] set parameter =
    set-function-body

// Alternative syntax for a property that has get and set.
[ attributes-for-get ]
[ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName =
    get-function-body
[ attributes-for-set ]
[ static ] member [accessibility-modifier-for-set] [self-identifier.]PropertyName
with set parameter =
    set-function-body

// Property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName =
    get-function-body

// Alternative syntax for property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with get() =
    get-function-body

// Property that has set only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with set parameter =
    set-function-body

// Automatically implemented properties.
[ attributes ]
[ static ] member val [accessibility-modifier] PropertyName = initialization-expression [ with get, set ]

Uwagi

Właściwości reprezentują relację "ma" w programowaniu obiektowym, reprezentującą dane skojarzone z wystąpieniami obiektów lub dla właściwości statycznych z typem.

Właściwości można zadeklarować na dwa sposoby, w zależności od tego, czy chcesz jawnie określić podstawową wartość (nazywaną również magazynem zapasowym) dla właściwości, czy też zezwolić kompilatorowi na automatyczne generowanie magazynu zapasowego. Ogólnie rzecz biorąc, należy użyć bardziej jawnego sposobu, jeśli właściwość ma implementację nietrywialną i automatyczny sposób, gdy właściwość jest po prostu prostą otoką wartości lub zmiennej. Aby jawnie zadeklarować właściwość, użyj słowa kluczowego member . Po tej składni deklaratywnej następuje get składnia określająca metody iset, również nazwane metody dostępu. Różne formy jawnej składni pokazanej w sekcji składni są używane do odczytu/zapisu, tylko do odczytu i zapisu oraz właściwości tylko do zapisu. W przypadku właściwości tylko do odczytu należy zdefiniować tylko metodę get . W przypadku właściwości tylko do zapisu zdefiniuj tylko metodę set . Należy pamiętać, że jeśli właściwość ma zarówno get metody dostępu, jak i set , składnia alternatywna umożliwia określenie atrybutów i modyfikatorów ułatwień dostępu, które są różne dla każdego metody dostępu, jak pokazano w poniższym kodzie.

// A read-only property.
member this.MyReadOnlyProperty = myInternalValue
// A write-only property.
member this.MyWriteOnlyProperty with set (value) = myInternalValue <- value
// A read-write property.
member this.MyReadWriteProperty
    with get () = myInternalValue
    and set (value) = myInternalValue <- value

W przypadku właściwości odczytu/zapisu, które mają zarówno metodę get , jak i set , kolejność get i set może zostać odwrócona. Alternatywnie można podać składnię wyświetlaną tylko dla get parametru i składnię wyświetlaną tylko dla set polecenia zamiast używać składni połączonej. Dzięki temu łatwiej jest komentować jednostkę get lub set metodę, jeśli jest to coś, co może być konieczne. Ta alternatywa dla używania połączonej składni jest pokazana w poniższym kodzie.

member this.MyReadWriteProperty with get () = myInternalValue
member this.MyReadWriteProperty with set (value) = myInternalValue <- value

Wartości prywatne, które przechowują dane dla właściwości, są nazywane magazynami zapasowymi. Aby kompilator automatycznie utworzyć magazyn zapasowy, użyj słów kluczowych member val, pomiń identyfikator własny, a następnie podaj wyrażenie, aby zainicjować właściwość. Jeśli właściwość ma być modyfikowalna, dołącz with get, setwartość . Na przykład następujący typ klasy zawiera dwie automatycznie zaimplementowane właściwości. Property1 jest tylko do odczytu i jest inicjowany do argumentu dostarczonego do konstruktora podstawowego i Property2 jest właściwością settable zainicjowaną na pusty ciąg:

type MyClass(property1 : int) =
member val Property1 = property1
member val Property2 = "" with get, set

Automatycznie zaimplementowane właściwości są częścią inicjowania typu, dlatego należy je uwzględnić przed wszelkimi innymi definicjami elementów członkowskich, podobnie jak let powiązania i do powiązania w definicji typu. Należy pamiętać, że wyrażenie, które inicjuje automatycznie zaimplementowaną właściwość, jest oceniane tylko podczas inicjowania, a nie za każdym razem, gdy uzyskuje się dostęp do właściwości. To zachowanie jest w przeciwieństwie do zachowania jawnie zaimplementowanej właściwości. Oznacza to, że kod inicjowania tych właściwości jest dodawany do konstruktora klasy. Rozważ następujący kod, który pokazuje tę różnicę:

type MyClass() =
    let random  = new System.Random()
    member val AutoProperty = random.Next() with get, set
    member this.ExplicitProperty = random.Next()

let class1 = new MyClass()

printfn $"class1.AutoProperty = %d{class1.AutoProperty}"
printfn $"class1.ExplicitProperty = %d{class1.ExplicitProperty}"

Wyjście

class1.AutoProperty = 1853799794
class1.AutoProperty = 1853799794
class1.ExplicitProperty = 978922705
class1.ExplicitProperty = 1131210765

Dane wyjściowe powyższego kodu pokazują, że wartość autowłaściwości jest niezmieniona po wywołaniu wielokrotnie, natomiast właściwość ExplicitProperty zmienia się za każdym razem, gdy jest wywoływana. Pokazuje to, że wyrażenie automatycznie zaimplementowanej właściwości nie jest obliczane za każdym razem, podobnie jak metoda getter dla właściwości jawnej.

Ostrzeżenie

Istnieją pewne biblioteki, takie jak Entity Framework (System.Data.Entity), które wykonują operacje niestandardowe w konstruktorach klasy bazowej, które nie działają dobrze z inicjowaniem automatycznie zaimplementowanych właściwości. W takich przypadkach spróbuj użyć jawnych właściwości.

Właściwości mogą być składowymi klas, struktur, związków dyskryminowanych, rekordów, interfejsów i rozszerzeń typów, a także mogą być definiowane w wyrażeniach obiektów.

Atrybuty można stosować do właściwości. Aby zastosować atrybut do właściwości, należy zapisać atrybut w osobnym wierszu przed właściwością. Aby uzyskać więcej informacji, zobacz Atrybuty.

Domyślnie właściwości są publiczne. Modyfikatory ułatwień dostępu można również stosować do właściwości. Aby zastosować modyfikator ułatwień dostępu, dodaj ją bezpośrednio przed nazwą właściwości , jeśli ma ona być stosowana zarówno get do metod , jak i , dodaj ją przed get słowami kluczowymi i setset, jeśli dla każdego dostępu są wymagane różne ułatwienia dostępu. Modyfikator ułatwień dostępu może być jednym z następujących elementów: public, , privateinternal. Aby uzyskać więcej informacji, zobacz Kontrola dostępu.

Implementacje właściwości są wykonywane za każdym razem, gdy uzyskuje się dostęp do właściwości.

Właściwości statyczne i wystąpienia

Właściwości mogą być statyczne lub właściwości wystąpienia. Właściwości statyczne mogą być wywoływane bez wystąpienia i są używane dla wartości skojarzonych z typem, a nie z poszczególnymi obiektami. W przypadku właściwości statycznych pomiń identyfikator własny. Identyfikator własny jest wymagany dla właściwości wystąpienia.

Poniższa definicja właściwości statycznej jest oparta na scenariuszu, w którym znajduje się pole myStaticValue statyczne, które jest magazynem zapasowym dla właściwości.

static member MyStaticProperty
    with get() = myStaticValue
    and set(value) = myStaticValue <- value

Właściwości mogą być również podobne do tablicy, w tym przypadku są nazywane właściwościami indeksowanych. Aby uzyskać więcej informacji, zobacz Właściwości indeksowane.

Adnotacja typu właściwości

W wielu przypadkach kompilator ma wystarczającą ilość informacji, aby wywnioskować typ właściwości z typu magazynu zaplecza, ale można jawnie ustawić typ, dodając adnotację typu.

// To apply a type annotation to a property that does not have an explicit
// get or set, apply the type annotation directly to the property.
member this.MyProperty1 : int = myInternalValue
// If there is a get or set, apply the type annotation to the get or set method.
member this.MyProperty2 with get() : int = myInternalValue

Używanie metod dostępu zestawu właściwości

Możesz ustawić właściwości zapewniające set metody dostępu przy użyciu <- operatora .

// Assume that the constructor argument sets the initial value of the
// internal backing store.
let mutable myObject = new MyType(10)
myObject.MyProperty <- 20
printfn "%d" (myObject.MyProperty)

Dane wyjściowe to 20.

Właściwości abstrakcyjne

Właściwości mogą być abstrakcyjne. Podobnie jak w przypadku metod, oznacza to, abstract że istnieje wirtualna wysyłka skojarzona z właściwością. Właściwości abstrakcyjne mogą być naprawdę abstrakcyjne, czyli bez definicji w tej samej klasie. Klasa zawierająca taką właściwość jest zatem klasą abstrakcyjną. Alternatywnie abstrakcja może oznaczać, że właściwość jest wirtualna, a w takim przypadku definicja musi znajdować się w tej samej klasie. Należy pamiętać, że właściwości abstrakcyjne nie mogą być prywatne, a jeśli jeden akcesor jest abstrakcyjny, drugi również musi być abstrakcyjny. Aby uzyskać więcej informacji na temat klas abstrakcyjnych, zobacz Klasy abstrakcyjne.

// Abstract property in abstract class.
// The property is an int type that has a get and
// set method
[<AbstractClass>]
type AbstractBase() =
    abstract Property1: int with get, set

// Implementation of the abstract property
type Derived1() =
    inherit AbstractBase()
    let mutable value = 10

    override this.Property1
        with get () = value
        and set (v: int) = value <- v

// A type with a "virtual" property.
type Base1() =
    let mutable value = 10
    abstract Property1: int with get, set

    default this.Property1
        with get () = value
        and set (v: int) = value <- v

// A derived type that overrides the virtual property
type Derived2() =
    inherit Base1()
    let mutable value2 = 11

    override this.Property1
        with get () = value2
        and set (v) = value2 <- v

Zobacz też