множественные конструкторы и наследование классов в F#

мне трудно преобразовать следующий код C# В F#:

class Foo
{
    public Foo() { }
    public Foo(string name) { }
}

class Bar : Foo
{
    public Bar() : base() { }
    public Bar(string name) : base(name) { }
    public string Name { get; set; }
}

Я сначала попытался следовать, но это сообщение об ошибке

конструкторы для типа " Bar " должны прямо или косвенно вызывать его неявный конструктор объектов. Использование вызова неявного объекта конструктор вместо выражения записи.

type Foo() =
    new(name:string) = Foo()

type Bar() =
    inherit Foo()
    new(name:string) = { inherit Foo(name) }
    member val Name:string = null with get, set

затем я попытался следовать, но теперь сообщает об ошибке в свойстве auto

определения'member val' разрешены только в типах с первичным конструктор. Рассмотрите возможность добавления аргументов в определение типа"

type Foo() =
    new(name:string) = Foo()

type Bar =
    inherit Foo
    new(name:string) = { inherit Foo(name) }
    member val Name:string = null with get, set

3 ответов


Если вы хотите исходный код F#, который компилируется в точно так же API как указано в вашем коде C#, ответ выглядит следующим образом:

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

type Bar =
    inherit Foo

    [<DefaultValue>] 
    val mutable private name:string

    new() = { inherit Foo() }
    new(name) = { inherit Foo(name) }

    member x.Name with get() = x.name and set v = x.name <- v

если класс имеет список параметров непосредственно после его имени (включая ()), имеет главный конструктор. Используя его, любой inherit объявления размещаются только в этом основном конструкторе, который приходит непосредственно после объявления класса и перед любым member декларации.

неясно, чего вы пытаетесь достичь. Класс!--5--> имеет конструктор, принимающий строковый аргумент, только чтобы отбросить его. A (технически) действительная, аналогичная пара занятия будут такие:

type Foo(name:string) =
    member f.NameLength = name.Length

type Bar(initialName) = // WARNING: this will not end well
    inherit Foo(initialName)
    member val Name:string = initialName with get, set

но это не разумный код. Foo сохранит начальное имя, даже если имя в Bar изменяется. Bar.Name.Length возвращает длину текущего имени, в то время как Bar.NameLength возвращает длину начального имени.

сохранить конструктор по умолчанию, можно добавить new () = Bar(null) (или эквивалент в Foo), но обратите внимание, что null считается функцией взаимодействия только. Он не используется в коде F# ; если возможно, используйте соответствующий тип параметра или пустую строку соответственно (в зависимости от того, является ли строка пустой или вообще не существует).

кроме того, наследование классов не рекомендуется в руководящих принципах проектирования компонентов F# - по уважительной причине. Существует несколько вариантов использования, но они обычно включают крошечный базовый класс и производный класс, который является его идеальным надмножеством. Гораздо чаще создавать типы, используя один класс в качестве члена другой.


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

type Text500(text : string) =
    do if text.Length > 500 then
        invalidArg "text" "Text of this type cannot have a length above 500."
    member t.Text = text
    new () = Text500("")

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


Это компилирует:

type Foo() =
    new(name:string) = Foo()

type Bar(name : string) =
    inherit Foo()
    new() = Bar(null) // or whatever you want as a default.
    member val Name:string = name with get, set

посмотреть Конструкторы (F#) и Наследование (F#).

глядя на декомпиляцию, C# будет (с удаленными атрибутами):

public class Bar : Program.Foo {
    internal string Name@;

    public string Name {
        get {
            return this.Name@;
        }
        set {
            this.Name@ = value;
        }
    }

    public Bar(string name) {
        this.Name@ = name;
    }

    public Bar() : this(null) {
    }
}

public class Foo {
    public Foo() {
    }

    public Foo(string name) : this() {
    }
}