7 Stimmen

Alternativer F#-Konstruktor, der (veränderbaren) let-Bindungen Werte zuweist

Angenommen, ich habe diese Klasse:

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

Ich möchte die Möglichkeit haben, eine neue Pet basierend auf einigen serialisierten Daten, die ich mit diesem Datensatz darstelle:

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

(TLDR: Ich kann nicht herausfinden, die Syntax, um einen Konstruktor zu machen, die ein nehmen wird PetData um die Bindungen zu füllen. Meine verschiedenen Versuche folgen).

Ich mache also eine neue Pet Konstruktor, der den Let-Bindings Werte zuweist. Ich versuche, die Syntax des Klasseninitialisierers zu verwenden:

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

Hmm, nein: 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.

Ich überprüfe, ob die Syntax stimmt: keine fehlenden Kommata, korrekte "Zuweisung" ( Husten ) Operator, korrekte Einrückung.

Okay, dann versuche ich es mal mit der Syntax für die Datensatzinitialisierung.

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

Fehler: The type 'Pet' does not contain a field 'name'

Okay, ich muss also den Hauptkonstruktor aufrufen. Ich denke, es gibt wahrscheinlich zwei Stellen, an denen ich ihn platzieren kann, also versuchen wir beide:

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

Nö: Invalid object, sequence or record expression

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

Und nein: 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.

Ich wollte dies nicht tun müssen, aber vielleicht, da die Felder ohnehin veränderbar sind, kann ich dem Objekt nach seiner Initialisierung einfach Werte zuweisen:

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, was?

Okay, versuchen wir es mal so:

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

Richtig, es kann also nicht von außen auf die Bindungen zugreifen.

Versuchen wir es also mit einem Mitglied:

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.

Okay... dann versuchen wir das Ganze mal anders, mit expliziten Feldern:

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

Und dann schlage ich meiner älteren Katze ins Gesicht.

Gibt es noch andere Ideen? Diese Fehlermeldungen verwirren mich. Ich kann nicht einmal die Hälfte von ihnen bei Google finden.

6voto

gradbot Punkte 13574

Sie könnten dies tun.

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# hat seinen eigenen Stil, der wie folgt aussieht.

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)

bearbeiten

Ein Ereignis hinzugefügt, das in do per Kommentaranfrage.

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)

1voto

David Grenier Punkte 1076

Let Bindings in einem Typ sind privat und es gibt nicht viel, was man dagegen tun könnte. Als solche können Sie keine Benannten Argumente verwenden. Durch das Erstellen von Eigenschaften können Sie dies tun, aber nicht innerhalb des Pet-Typs:

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)

Die andere Möglichkeit wäre, einen allgemeineren Konstruktor zu erstellen, wie von Gradbot vorgeschlagen, der entweder direkt ein PetData-Objekt oder alle drei Parameter akzeptiert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X