2014-12-01 3 views
5

Я с трудом преобразовать следующие C# код для F #:несколько конструкторов и наследование классов в 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 

Затем я попытался следующие, но теперь сообщает об ошибке на авто собственности

определения «члена 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 

ответ

5

Если вы хотите, чтобы исходный код 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 
+0

В этом случае вы по-прежнему не можете добавить следующее: member val Name = null with get, set – wangzq

+0

Да, я это наблюдал. Я редактировал класс Bar, чтобы включить свойство Name. Это не авто-свойство, но API и семантика точно такие же, как в случае с C#. Другого способа воспроизведения вашего примера C# «на букву» нет. –

+0

Согласен; это ближайший обходной момент в настоящий момент. – wangzq

1

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

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 

См Constructors (F#) и Inheritance (F#)

Глядя на декомпиляции, С # будет (. с атрибутами удалены):

public class Bar : Program.Foo { 
    internal string [email protected]; 

    public string Name { 
     get { 
      return [email protected]; 
     } 
     set { 
      [email protected] = value; 
     } 
    } 

    public Bar(string name) { 
     [email protected] = name; 
    } 

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

public class Foo { 
    public Foo() { 
    } 

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

Thanks; но это уже не тот же код на C#, верно? Я понимаю, что пример кода на C# не является реальным кодом, но дело в том, чтобы увидеть, можем ли мы иметь точную логику, реализованную в F # ... Я соглашусь на ваш ответ в течение нескольких дней, если нет других хороших альтернатив этой проблеме. – wangzq

+0

Это действительно не совсем то же самое ('Bar.Name' получает значение от конструктора принятия' string'), но - IMO - имеет больше смысла (даже если бы свойство 'Name' было в базовом типе). И из cour5se, некоторые из различий разобраны IL показывает детали реализации поля. – Richard

1

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

Непонятно, чего вы пытаетесь достичь. Класс Foo имеет конструктор, принимающий строковый аргумент, только для его удаления. 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), но обратите внимание, что нуль считается Interop только особенность. Он не используется в коде 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("") 

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

+0

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

Смежные вопросы