F#alternate constructor赋值给(mutable)let绑定

Rei*_*aka 6 f# constructor

假设我有这个课程:

type Pet (name:string) as this =
    let mutable age = 5
    let mutable animal = "dog"
Run Code Online (Sandbox Code Playgroud)

我希望能够Pet基于一些序列化数据创建一个新的,我用这个记录表示:

type PetData = {
    name : string
    age : int
    animal : string
}
Run Code Online (Sandbox Code Playgroud)

(TLDR:我无法弄清楚构建一个构造函数的语法,它将PetData填充let绑定.我的各种尝试都会随之而来.)

所以我创建了一个新的Pet构造函数,它将为let绑定赋值.我尝试使用类初始化器语法:

new (data:PetData) =
    Pet(name,
        age = data.age,
        animal = data.animal
    )
Run Code Online (Sandbox Code Playgroud)

嗯,不, No accessible member or object constructor named 'Pet' takes 1 arguments. The named argument 'age' doesn't correspond to any argument or settable return property for any overload.

我检查以确保我已经掌握了所有语法:没有错过的逗号,正确的"赋值"(咳嗽)操作符,正确的缩进.

好的,我会尝试记录初始化语法.

new (data:PetData) =
    {
        name = data.name;
        age = data.age;
        animal = data.name
    }
Run Code Online (Sandbox Code Playgroud)

错误: The type 'Pet' does not contain a field 'name'

好的,所以我需要调用主构造函数.我想我可以放两个地方,所以让我们试试两个:

new (data:PetData) =
    {
        Pet(data.name);
        age = data.age;
        animal = data.name
    }
Run Code Online (Sandbox Code Playgroud)

不: Invalid object, sequence or record expression

new (data:PetData) =
    Pet(data.name)
    {
        age = data.age;
        animal = data.name
    }
Run Code Online (Sandbox Code Playgroud)

不,不: This is not a valid object construction expression. Explicit object constructors must either call an alternate constructor or initialize all fields of the object and specify a call to a super class constructor.

我不想这样做,但也许因为字段是可变的,我可以在初始化之后为对象分配值:

new (data:PetData) =
    let p = Pet(data.name)
    p.age <- data.age
    p.animal <- data.animal
    p
Run Code Online (Sandbox Code Playgroud)

Type constraint mismatch. The type Pet is not compatible with type PetData The type 'Pet' is not compatible with the type 'PetData'

笑什么??

好的,我们试试这个:

let assign(data:PetData) =
    this.age <- data.age
    this.animal <- data.animal

new (data:PetData) =
    let p = Pet(data.name)
    p.assign(data)
    p
Run Code Online (Sandbox Code Playgroud)

The field, constructor or member 'assign' is not defined

是的,所以它无法从外部访问let绑定.

让我们尝试一个成员:

new (data:PetData) =
    let p = Pet(data.name)
    p.Assign(data)
    p

member x.Assign(data:PetData) =
    this.age <- data.age
    this.animal <- data.animal
Run Code Online (Sandbox Code Playgroud)

This is not a valid object construction expression. Explicit object constructors must either call an alternate constructor or initialize all fields of the object and specify a call to a super class constructor.

好吧......让我们尝试使用显式字段来完成不同的事情:

type Pet =
    [<DefaultValue>]val mutable private age : int
    [<DefaultValue>]val mutable private animal : string
    val private name : string

    new(name:string) =
        { name = name }

    new(data:PetData) =
        {
            name = data.name;
            age = data.age;
            animal = data.animal
        }
Run Code Online (Sandbox Code Playgroud)

Extraneous fields have been given values

那就是我把老猫打在脸上的时候.

还有其他想法吗?这些错误消息让我失望.我甚至无法在Google上找到其中一半.

gra*_*bot 5

你可以做到这一点。

type Pet =
    val mutable private age : int
    val mutable private animal : string
    val private name : string

    new (name:string) =
        { 
            name = name;
            age = 5; // or age = Unchecked.defaultof<_>;
            animal = "dog"; // or animal = Unchecked.defaultof<_>;
        }

    new (data:PetData) =
        {
            name = data.name;
            age = data.age;
            animal = data.animal;
        }
Run Code Online (Sandbox Code Playgroud)

F#具有自己的样式,如下所示。

type Pet(name:string, age:int, animal:string) =
    let mutable age = age
    let mutable animal = animal

    new (name:string) =
        Pet(name, 5, "dog")

    new (data:PetData) =
        Pet(data.name, data.age, data.animal)
Run Code Online (Sandbox Code Playgroud)

编辑

添加了do每个评论请求中使用的事件。

type Pet(name:string, age:int, animal:string, start:IEvent<string>) =
    let mutable age = age
    let mutable animal = animal

    // all three constructors will call this code.
    do  start.Add (fun _ -> printf "Pet was started")

    new (name:string, start:IEvent<_>) =
        // an example of different logic per constructor
        // this is called before the `do` code.
        let e = start |> Event.map (fun x -> x + " from 'name constructor'")
        Pet(name, 5, "dog", e)

    new (data:PetData, start:IEvent<_>) =
        Pet(data.name, data.age, data.animal, start)
Run Code Online (Sandbox Code Playgroud)