레코드 스프레드 연산자

고유한 모델 형식을 병합하지 않고 필드를 공유하려면 F# 11부터 사용할 수 있는 레코드 스프레드를 사용합니다.

필드 선언을 새 명명된 레코드에 기여하거나 ...expression 필드 값을 명명된 레코드 또는 익명 레코드에 기여하는 데 사용합니다...Type. 선언 재사용 및 값 매핑은 독립적인 선택입니다.

레코드 형식 작성

구성 유형 간에 연결 필드를 공유하려면 새 레코드 선언으로 분산 Endpoint 합니다.

type Endpoint = { Host: string; Port: int }

type ClientOptions =
    { ...Endpoint
      TimeoutSeconds: int }

ClientOptions 가 Host, Port및 TimeoutSeconds. 하위 형식이 아닌 고유한 명목 형식입니다 Endpoint .

여러 소스 및 명시적 선언이 동일한 형식 정의에 나타날 수 있습니다.

type Logging = { LogLevel: string }
type LoggedClientOptions = { ...ClientOptions; ...Logging }

레코드 필드만 참여합니다. 메서드, 레코드 필드가 아닌 속성 및 정적 멤버는 복사되지 않습니다. 원본은 익명 레코드의 약어를 포함하여 명명된 레코드 또는 익명 레코드 형식일 수 있습니다. 형식 스프레드의 대상은 명명된 레코드 형식이어야 합니다.

레코드 값 작성

클라이언트 옵션에 엔드포인트를 복사한 다음, 익명 레코드를 사용하여 호스트를 대체합니다.

let local = { Host = "localhost"; Port = 5432 }
let options: ClientOptions = { ...local; TimeoutSeconds = 30 }

let remote = {| ...options; Host = "db.example.com" |}

printn $"{remote.Host}:{remote.Port}, timeout {remote.TimeoutSeconds}s"
// db.example.com:5432, timeout 30s

식 스프레드는 구조체 레코드를 포함하여 명명되거나 익명의 레코드 원본 및 대상을 허용합니다. 각 대상 필드는 값을 받아야 합니다. 명시적 대상 주석은 여러 명명된 레코드 형식이 레이블을 공유하는 경우에 유용합니다.

필드의 형식 강화

이후 필드 선언은 스프레드 필드의 형식을 바꿀 수 있습니다. 예를 들어 구문 분석된 식별자를 type CustomerId = CustomerId of System.Guid나타내는 다음 가져온 레코드를 해당 식별자가 있는 레코드와 구분합니다.

type ImportedCustomer = { Name: string; Id: string }
type IdentifiedCustomer = { ...ImportedCustomer; Id: CustomerId }

let identify (source: ImportedCustomer) =
    match Guid.TryParse source.Id with
    | true, id -> Ok ({ ...source; Id = CustomerId id }: IdentifiedCustomer)
    | false, _ -> Error "Invalid customer identifier."

Guid.TryParse 텍스트의 유효성을 검사합니다. 스프레드는 필드를 복사합니다. CustomerId 는 추가 규칙을 부과하지 않고 식별자를 다른 GUID와 구분합니다.

독립 모델 결합

큐레이팅된 명목 레코드 및 익명 레코드 형식에서 선언을 작성한 다음, 독립 소스의 값을 어셈블합니다.

type PublicProductFields = { Name: string; UnitPrice: decimal }
type ProductRow = { ...PublicProductFields; InternalCost: decimal }
type StockCounts = {| InStock: int; Reserved: int |}

type ProductCard =
    { ...PublicProductFields
      ...StockCounts
      CanOrder: bool }
let product: ProductRow =
    { Name = "Notebook"; UnitPrice = 12.50m; InternalCost = 4m }
let stock: StockCounts = {| InStock = 8; Reserved = 2 |}

let card: ProductCard =
    { ...product; ...stock; CanOrder = stock.InStock > stock.Reserved }

let viewState = {| ...card; Selected = false |}
let selected = {| ...viewState; Selected = true |}

명명된 소스와 익명 소스가 제공됩니다 ProductCard. 그러면 카드는 익명 보기 상태를 제공합니다. CanOrder 는 한 번 계산되며, 이후 생성 시 개수가 변경될 때 다시 계산되지 않습니다.

알려진 대상에 Project

대상 유형은 유지할 필드를 선택합니다. 여러 원본에서 대체 기능을 제공할 수 있습니다.

let endpoint: Endpoint = { ...options }
let overrides = {| Host = "db.example.com"; Port = 5433 |}
let production: ClientOptions =
    { ...options; ...overrides; TimeoutSeconds = 60 }

printn $"{endpoint.Host}:{endpoint.Port}"
printn $"{production.Host}:{production.Port}, timeout {production.TimeoutSeconds}s"
// localhost:5432
// db.example.com:5433, timeout 60s

endpoint는 Host 원본 및 Portproduction 명시적 재정의만을 결합합니다. 대상에 없는 명시적 필드는 오류로 남아 있습니다.

주석이 지정되지 않은 익명 레코드는 모든 기여 필드를 사용합니다. 명명된 대상은 유추할 수 있어야 합니다. 알 수 없는 원본에는 함수 매개 변수와 같은 (source: ClientOptions) 고유한 주석이 필요합니다.

필드 순서 및 재정의

기여는 왼쪽에서 오른쪽으로 읽습니다. 반복되는 필드 이름의 경우 가장 오른쪽에 있는 기여가 우선합니다. 규칙은 형식의 필드 선언과 식의 필드 값에 모두 적용됩니다.

let laterHost = {| Host = "db.example.com" |}
let corrected: Endpoint = { ...local; ...laterHost; Port = 5433 }

형식 재정의 후 대체 형식의 값을 제공합니다. 반복된 명시적 필드(예: { Host = "first"; Host = "second" }오류)가 남아 있습니다.

형식 선언의 경우 승리 필드가 우승 기여의 위치에 표시됩니다. 원본의 필드는 원본 순서를 유지합니다. 이 순서는 생성자 및 서명 준수에 중요합니다.

평가 및 복사

스프레드 소스 식은 나중에 기여한 내용이 제공하는 모든 필드를 대체하는 경우에도 원본 순서로 각각 한 번 실행됩니다.

let visits = ResizeArray<string>()
let hostFrom source host =
    visits.Add source
    {| Host = host |}

let selected =
    {| ...hostFrom "first" "localhost"
       ...hostFrom "second" "db.example.com" |}

assert (selected.Host = "db.example.com")
assert (Seq.toList visits = [ "first"; "second" ])

스프레드는 단순 필드 복사본과 명시적 할당과 동일한 변환을 사용하여 새 레코드를 만듭니다. 필드에서 참조하는 변경 가능한 개체는 공유된 상태로 유지됩니다.

중첩 필드 업데이트는 복사 및 업데이트와 마찬가지로 스프레드에서 작동합니다.

let original = {| Connection = local; Retries = 3 |}
let changed = {| ...original; Connection.Port = 5433 |}

assert (changed.Connection.Port = 5433)
assert (original.Connection.Port = 5432)

나중에 해당 필드에 대한 이전 중첩 업데이트를 대체하는 데 기여 Connection 합니다. 재정의해야 하는 스프레드 후에 신중한 재정의를 배치합니다.

그림자 필드에 대한 경고

이후 스프레드는 실수로 명시적 선택을 취소할 수 있습니다. 컴파일러는 기본적으로 다음과 같은 경고를 보고합니다.

Warning Meaning
FS3897 형식의 필드가 이전 명시적 필드 선언을 숨깁니다.
FS3898 식의 필드가 이전 명시적 필드 바인딩을 숨깁니다.

예를 들어 . { Host = "db.example.com"; ...local }Hostlocal"db.example.com"

다른 중복에 대한 감사의 경우 다음 옵트인 경고를 사용하도록 설정합니다.

Warning Meaning
FS3905 형식 스프레드는 이전 형식 스프레드의 필드를 숨깁니다.
FS3906 명시적 필드는 이전 스프레드의 필드를 숨깁니다.
FS3907 식 스프레드는 이전 식 스프레드의 필드를 숨깁니다.
<WarnOn>$(WarnOn);3905;3906;3907</WarnOn>

이러한 알림은 맨 오른쪽 승리 동작을 변경하지 않습니다.

표현 및 API 호환성

형식은 복사 필드 변경 가능성 및 필드 수준 특성을 분산합니다. 형식 수준 특성은 복사하지 않습니다. 대상의 고유한 [<Struct>] 특성에 따라 해당 표현이 결정됩니다. 원본 형식 인수는 측정 단위 제약 조건을 포함하여 원본의 제네릭 제약 조건을 충족해야 합니다.

Important

공유된 진화가 의도적인 경우에만 형식 스프레드를 사용합니다. 원본 필드 변경은 대상에 대한 편집 없이 대상의 공용 필드, 생성자, 특성 또는 제약 조건을 변경할 수 있습니다.

스프레드는 접근성 검사를 무시하지 않습니다. Nullable 레코드는 유효한 분산 원본이 아닙니다. 은 nullable 값을 사용하기 전에 null이 아닌 값으로 설정합니다. 스프레드를 통한 순환 종속성은 거부됩니다. 일반 재귀 레코드 필드는 선언에서 스프레드를 사용하기 때문에 금지되지 않습니다.

형식 스프레드는 파일에서 .fsi 작동합니다. 결과 필드와 순서가 동의하는 경우 구현에서 스프레드를 사용하는 동안 서명은 명시적 필드를 사용할 수 있습니다. 따옴표에는 스프레드 노드가 아닌 정교한 레코드 생성이 포함됩니다.

스프레드 선택 또는 복사 및 업데이트

한 원본에서 동일한 형식 의 업데이트에 복사 및 업데이트 식을 사용합니다. 스프레드를 사용하여 원본, 프로젝트를 다른 레코드 형식으로 결합하거나 필드 선언을 다시 사용합니다. 동일한 레코드 식과 결합 with... 하지 마세요.

스프레드는 컬렉션, 개체 식, 임의 .NET 개체 또는 C# 레코드에는 적용되지 않습니다. 레코드 패턴 구문을 제공하지 않습니다. 레코드 생성자는 별도의 F# 11.2 기능입니다.

또한, 다음을 참조하세요.