2011-12-26 14 views
7

Ten fragment F # koduObiekty rekursywne w 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 

skutkuje następującym ostrzeżeniem:

traynote.fs (62,41): FS0040 Uwaga: Ten i inne rekurencyjne odniesienia do obiektu (środkami chemicznymi) zdefiniowany zostanie sprawdzony pod kątem poprawności inicjalizacji - w czasie wykonywania poprzez użycie opóźnionego odwołania. Wynika to z tego, że definiujesz jeden lub więcej obiektów rekurencyjnych, a nie rekurencyjnych. To ostrzeżenie może zostać stłumione przez użycie "#nowarn" 40 "lub" --nowarn: 40 ".

Czy istnieje sposób na przepisanie kodu w celu uniknięcia tego ostrzeżenia? Czy nie ma koszernego sposobu na posiadanie obiektów rekurencyjnych w F #?

Odpowiedz

14

Twój kod jest doskonałym sposobem na skonstruowanie obiektu rekursywnego. Kompilator generuje ostrzeżenie, ponieważ nie może zagwarantować, że odwołanie nie będzie dostępne przed zainicjowaniem (co spowodowałoby błąd w czasie wykonywania). Jednakże, jeśli wiesz, że EventHandler nie wywołuje podanej funkcji lambda podczas budowy (nie ma), możesz bezpiecznie zignorować ostrzeżenie.

Aby dać przykład, gdzie ostrzeżenie faktycznie wskazuje na problemy, można spróbować następujący kod:

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

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

Klasa Evil wykonuje funkcję w konstruktorze i wzywa go podczas budowy. W rezultacie referencja rekurencyjna w funkcji lambda próbuje uzyskać dostęp do e, zanim zostanie ustawiona na wartość (i pojawi się błąd runtime). Jednak, szczególnie podczas pracy z procedurami obsługi zdarzeń, nie stanowi to problemu (a ostrzeżenie pojawia się, gdy poprawnie używasz obiektów rekursywnych).

Jeśli chcesz pozbyć się ostrzeżenia, można przepisać kod za pomocą wyraźnych ref wartości i korzystania null, ale wtedy będziesz w tym samym niebezpieczeństwo błędu wykonawczego, tylko bez ostrzeżenia i brzydsze kodu :

let foo (evt:IEvent<_, _>) = 
    let eh = ref null 
    eh := new EventHandler(fun _ _ -> 
    evt.RemoveHandler(!eh)) 
    evt.AddHandler(!eh)