2014-12-01 27 views
5

Tôi gặp khó khăn để chuyển đổi sau mã C# để F #:nhiều nhà xây dựng, thừa kế lớp trong 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; } 
} 

đầu tiên tôi đã cố gắng sau, nhưng nó được báo cáo lỗi

Constructors cho các loại ' Bar 'phải trực tiếp hoặc gián tiếp gọi hàm xây dựng đối tượng ẩn của nó là . Sử dụng lệnh gọi đối tượng ẩn hàm tạo thay vì biểu thức ghi.

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

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

Sau đó, tôi đã cố gắng sau, nhưng nó bây giờ được báo cáo lỗi trên thuộc tính tự động

định nghĩa 'thành viên val' chỉ được phép trong các loại với một constructor chính. Hãy xem xét thêm lập luận để định nghĩa kiểu của bạn"

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

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

Trả lời

5

Nếu bạn muốn mã nguồn F # biên dịch thành chính xác cùng một mã như được cung cấp bởi mã C# của bạn, câu trả lời như sau:

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

Trong trường hợp này bạn vẫn không thể thêm sau đây: thành viên val Tên = null với get, thiết lập – wangzq

+0

Có, tôi giám sát điều đó. Tôi đã chỉnh sửa lớp Bar để bao gồm một thuộc tính Name. Nó không phải là thuộc tính tự động, nhưng API và ngữ nghĩa chính xác giống như trong trường hợp C#. Không có cách nào khác để tái tạo ví dụ C# của bạn "thành chữ". –

+0

Đồng ý; đây là cách giải quyết gần nhất tại thời điểm hiện tại. – wangzq

1

này biên dịch:

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 

Xem Constructors (F#)Inheritance (F#)

Nhìn vào decompilation, C# sẽ là (. với các thuộc tính đã bị xóa):

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

Cảm ơn; nhưng điều này không còn là cùng một mã C#, đúng không? Tôi hiểu ví dụ mã C# không phải là mã thực nhưng điểm là để xem liệu chúng ta có thể có logic chính xác được triển khai trong F # hay không ... Tôi sẽ chấp nhận câu trả lời của bạn trong vài ngày nếu không có lựa chọn thay thế nào khác cho vấn đề này. – wangzq

+0

Nó thực sự không hoàn toàn giống nhau ('Bar.Name' được đưa ra một giá trị từ' string' lấy constructor), nhưng - IMO - có ý nghĩa hơn (thậm chí nhiều hơn nếu thuộc tính 'Name' nằm trong kiểu cơ sở). Và của cour5se, một số sự khác biệt là tháo rời IL cho thấy các chi tiết thực hiện của lĩnh vực này. – Richard

1

Nếu một lớp có danh sách tham số ngay sau tên của nó (bao gồm ()), nó có một nhà xây dựng chính chính. Sử dụng nó, bất kỳ khai báo inherit nào chỉ được đặt trong hàm dựng chính này, trực tiếp sau khai báo lớp và trước bất kỳ khai báo member nào.

Không rõ bạn đang cố gắng đạt được điều gì. Lớp Foo có một hàm tạo tham số chuỗi, chỉ để loại bỏ nó. Một cặp (tương tự) hợp lệ, có giá trị tương tự sẽ là:

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 

Nhưng đây không phải là mã hợp lý.Foo sẽ giữ nguyên tên ban đầu ngay cả khi tên trong Bar bị thay đổi. Bar.Name.Length trả về độ dài của tên hiện tại, trong khi Bar.NameLength trả về độ dài của tên ban đầu.

Để giữ cho constructor mặc định, người ta có thể thêm new() = Bar(null) (hoặc tương đương trong Foo), nhưng xin lưu ý rằng null được coi là một interop chỉ đặc trưng. Nó không được sử dụng trong mã F # phải đối mặt; nếu có thể, hãy sử dụng loại tùy chọn thích hợp hoặc một chuỗi trống tương ứng (tùy thuộc vào việc chuỗi chỉ rỗng hoặc không tồn tại chút nào).

Ngoài ra, các lớp kế thừa không được khuyến khích trong nguyên tắc thiết kế thành phần F # - vì lý do chính đáng. Có rất ít trường hợp sử dụng, nhưng những trường hợp này thường liên quan đến một lớp cơ sở nhỏ và một lớp dẫn xuất là một phần siêu hoàn hảo của nó. Sắp xếp các loại bằng cách sử dụng một lớp là một thành viên của một nhóm khác phổ biến hơn nhiều.


Tôi không biết làm thế nào có liên quan này, nhưng đây là một ví dụ về một lớp học với constructor mặc định và một nhà xây dựng bổ sung mà sử dụng nó:

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("") 

này sử dụng các nhà xây dựng chính để xác minh đầu vào và có thêm một hàm tạo tham số, sử dụng một chuỗi rỗng. (Tôi không chắc liệu hàm tạo bổ sung có hữu ích trong các ứng dụng thực tế không.)

+0

Tôi đang cố gắng để làm việc với WPF và tôi nghi ngờ các nhà xây dựng có sản phẩm nào ở đây sẽ giúp với điều đó. –

Các vấn đề liên quan