2011-12-26 41 views
7

đoạn này của F # mãCác đối tượng đệ quy trong F #?

let rec reformat = new EventHandler(fun _ _ -> 
     b.TextChanged.RemoveHandler reformat 
     b |> ScrollParser.rewrite_contents_of_rtb 
     b.TextChanged.AddHandler reformat 
     ) 
    b.TextChanged.AddHandler reformat 

kết quả trong cảnh báo sau đây:

traynote.fs (62,41): FS0040 cảnh báo: Đây và tài liệu tham khảo đệ quy khác để đối tượng (s) là được xác định sẽ được kiểm tra về độ ổn định khởi tạo khi chạy thông qua việc sử dụng tham chiếu bị trì hoãn. Điều này là do bạn đang xác định một hoặc nhiều đối tượng đệ quy, chứ không phải là các hàm đệ quy. Cảnh báo này có thể bị chặn bằng cách sử dụng '#nowarn' 40 '' hoặc '--nowarn: 40'.

Có cách nào để viết lại mã để tránh cảnh báo này không? Hoặc là không có cách nào kosher có đệ quy đối tượng trong F #?

Trả lời

14

Mã của bạn là một cách hoàn toàn tốt để xây dựng một đối tượng đệ quy. Trình biên dịch phát ra một cảnh báo, bởi vì nó không thể đảm bảo rằng tham chiếu sẽ không được truy cập trước khi nó được khởi tạo (điều này sẽ gây ra lỗi thời gian chạy). Tuy nhiên, nếu bạn biết rằng EventHandler không gọi hàm lambda được cung cấp trong quá trình xây dựng (nó không), thì bạn có thể bỏ qua cảnh báo một cách an toàn.

Để đưa ra một ví dụ trong đó cảnh báo thực sự cho thấy một vấn đề, bạn có thể thử đoạn mã sau:

type Evil(f) = 
    let n = f() 
    member x.N = n + 1 

let rec e = Evil(fun() -> 
    printfn "%d" (e:Evil).N; 1) 

Lớp Evil mất một hàm trong một constructor và gọi đó là khi thi công. Kết quả là, tham chiếu đệ quy trong hàm lambda cố gắng truy cập e trước khi nó được đặt thành một giá trị (và bạn sẽ nhận được một lỗi thời gian chạy). Tuy nhiên, đặc biệt là khi làm việc với các trình xử lý sự kiện, đây không phải là một vấn đề (và bạn nhận được cảnh báo khi bạn đang sử dụng các đối tượng đệ quy một cách chính xác).

Nếu bạn muốn loại bỏ cảnh báo, bạn có thể viết lại mã bằng cách sử dụng giá trị ref rõ ràng và sử dụng null, nhưng sau đó bạn sẽ gặp nguy hiểm tương tự với lỗi thời gian chạy, không cần cảnh báo và mã xấu hơn :

let foo (evt:IEvent<_, _>) = 
    let eh = ref null 
    eh := new EventHandler(fun _ _ -> 
    evt.RemoveHandler(!eh)) 
    evt.AddHandler(!eh) 
Các vấn đề liên quan