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

时间:2011-12-17 02:18:55

标签: f# constructor

假设我有这门课程:

type Pet (name:string) as this =
    let mutable age = 5
    let mutable animal = "dog"

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

type PetData = {
    name : string
    age : int
    animal : string
}

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

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

new (data:PetData) =
    Pet(name,
        age = data.age,
        animal = data.animal
    )

嗯,不,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.

我检查以确保我已经掌握了所有语法:没有错过的逗号,正确的“赋值”( cough )运算符,正确的缩进。

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

new (data:PetData) =
    {
        name = data.name;
        age = data.age;
        animal = data.name
    }

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

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

new (data:PetData) =
    {
        Pet(data.name);
        age = data.age;
        animal = data.name
    }

不:Invalid object, sequence or record expression

new (data:PetData) =
    Pet(data.name)
    {
        age = data.age;
        animal = data.name
    }

and nope: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

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

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

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
        }

Extraneous fields have been given values

那时我正打着老猫的脸。

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

2 个答案:

答案 0 :(得分:4)

你可以这样做。

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;
        }

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)

修改

在每个评论请求中添加了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)

答案 1 :(得分:1)

让类型中的绑定是私有的,你可以做的不多。因此,您不能使用命名参数。通过创建属性,您可以这样做,但不能从Pet类型内部执行:

type Pet (name:string) =
    let mutable age = 5
    let mutable animal = "dog"

    member x.Age with get () = age and set v = age <- v
    member x.Animal with get () = animal and set v = animal <- v

type PetData = {
    name : string
    age : int
    animal : string
}
with
    member x.ToPet =
        new Pet (x.name, Age = x.age, Animal = x.animal)

另一种选择是创建一个像Gradbot建议的更通用的构造函数,直接接受PetData对象或者接受所有三个参数。