F # альтернативный конструктор, присваивающий значения (изменяемым) let bindings - PullRequest
6 голосов
/ 17 декабря 2011

Предположим, у меня есть этот класс:

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

Я хочу иметь возможность создать новый Pet на основе некоторых сериализованных данных, которые я представляю с этой записью:

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

(TLDR: я не могу понять синтаксис для создания конструктора, который будет заполнять привязки let PetData. Мои различные попытки следуют.)

Итак, я делаю новый 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.

Я проверяю, чтобы убедиться, что у меня есть весь синтаксис: нет пропущенных запятых, правильное "назначениеОператор "( кашель ), правильный отступ.

Хорошо, я попробую синтаксис инициализатора записи.

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
    }

И нет: 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'

Lol, что ??

Хорошо, давайте попробуем это:

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 ]

4 голосов
/ 17 декабря 2011

Вы могли бы сделать это.

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 голос
/ 17 декабря 2011

Пусть привязки в типе являются частными, и вы мало что можете с этим поделать.Таким образом, вы не можете использовать именованные аргументы.Создавая свойства, вы можете сделать это следующим образом, но не изнутри типа 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 напрямую или всетри параметра.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...