Jak uzyskać dostęp do nazwanych pól członka grupy dyskryminowanej?Dostęp do nazwanych pól członka DU
Przykład:
type Point = | Point of x : int * y : int
let p = Point(3, 1)
// how to access x-value or y-value of p here?
Jak uzyskać dostęp do nazwanych pól członka grupy dyskryminowanej?Dostęp do nazwanych pól członka DU
Przykład:
type Point = | Point of x : int * y : int
let p = Point(3, 1)
// how to access x-value or y-value of p here?
Ogólnie, związki z wymienionych dziedzin działa podobnie jak każdy inny rodzaj związków: by uzyskać dostęp do pól poprzez match
:
let x, y =
match p with
| Point (x1, y1) -> x1, y1
The F# documentation wspomina również sposobu dopasowywania tylko niektóre nazwane parametry. W twoim przypadku oznacza to, można napisać:
let xOnly =
match p with
| Point (x = x1) -> x1
Jeśli wszystko masz to pojedynczy przypadek, patrz odpowiedź od @ p.s.w.g. Kod w tej odpowiedzi jest ogólny dla wszystkich dyskryminowanych związków. Dla pojedynczych związków z wymienionych dziedzin, można użyć specjalnej składni przedstawionej powyżej i napisać:
let Point(x = myX) = p
To wiąże wartość pola x
do myX
.
PS zgodnie z komentarzem: Dlaczego nie można odczytać pól od razu, wykonując p.x
? Możesz myśleć o dyskryminowanej unii jako małej hierarchii obiektów (i używać jej, zobacz discriminated union documentation: "Często można użyć dyskryminowanej unii jako prostszej alternatywy dla małej hierarchii obiektów"). Rozważmy następujący DU:
type Shape = | Circle of r: float | Square of float
Można zobaczyć Shape
jako nadrzędnej. Circle
i Square
to dwie pochodne klasy, każda z jedną właściwością float
. Każde utworzone wystąpienie Circle
lub Square
zostanie zaktualizowane do wersji Shape
.
Dzięki temu staje się jasne, dlaczego nie można od razu odczytać pól: najpierw trzeba ustalić, które z klas pochodnych, na które patrzysz, tylko po rzutowaniu na prawą podklasę możesz odczytać pola .
Obiekt ten widok hierarchia odpowiada bardzo dokładnie, w jaki sposób dalsi są obsługiwane wewnętrznie przez F #: Jeśli spojrzeć na rodzaj DU w odbiciu, widać dwie zagnieżdżone typy, które mają taką samą nazwę jak swoich sprawach związkowych:
> typeof<Shape>.GetNestedTypes()
|> Seq.iter (fun t ->
let p = t.GetProperties()
let s =
p
|> Array.map (fun p -> sprintf "%s: %s" p.Name p.PropertyType.Name)
|> String.concat "; "
printfn "Nested type %s: %i Properties %s" t.Name p.Length s
);;
Nested type Tags: 0 Properties
Nested type Circle: 4 Properties r: Double; Tag: Int32; IsCircle: Boolean; IsSquare: Boolean
Nested type Square: 4 Properties Item: Double; Tag: Int32; IsCircle: Boolean; IsSquare: Boolean
Rzeczywiste dane dotyczą właściwości podklasy. Dla Circle
użyliśmy nazwanych pól, zobaczysz właściwość r
. W przypadku Square
mamy dane w własności Item
(lub Item1
, Item2
, jeśli było wiele argumentów). Reszta to generowane kompilatory: pole numeryczne Tag
, które będzie używane do szybkiego rozróżniania podklas i dwóch właściwości bool dla kontroli podklas.
Sam nadklasą ma tylko kompilatora generowane właściwości:
> typeof<Shape>.GetProperties()
|> Seq.iter (fun p -> printfn "Property %s" p.Name);;
Property Tag
Property IsCircle
Property IsSquare
Dla pojedynczych przypadków dyskryminowanych związków jak twój przykład nie trzeba używać wyrażenia match-with
.może po prostu to zrobić:
let (Point (x, y)) = p
printf "%i" x // 3
Albo po prostu x
i ignorować y
:
let (Point (x, _)) = p
printf "%i" x // 3
nie jest krótsza składnia do osiągnięcia czegoś jak 'niech P2 = Point (px, py)' ? –
W ogólnym przypadku, z wieloma przypadkami związków: nie. 'p' może być obiektem, który reprezentuje przypadek' Point', ale także kilka innych przypadków unii. Jeśli chcesz myśleć o tym w terminach OOP: 'p' jest instancją superklasy, ale próbujesz uzyskać dostęp do pól klasy pochodnej. 'Dopasowanie' ma odpowiednik rzutowania i udostępnia pola. –
@no_mindset, jeśli masz tylko jeden przypadek i interesuje cię krótsza składnia, używaj rekordów, a nie związków. –