2011-12-26 8 views
7

questo frammento di codice F #Oggetti ricorsivi in ​​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 

risultati nella seguente avviso:

traynote.fs (62,41): FS0040 avvertimento: Questo e altri riferimenti ricorsivi all'oggetto (s) essendo definito verrà verificato per l'inizializzazione-solidità in fase di esecuzione attraverso l'uso di un riferimento ritardato. Questo perché stai definendo uno o più oggetti ricorsivi, piuttosto che funzioni ricorsive. Questo avviso può essere soppresso usando "#nowarn" 40 "" o "--nowarn: 40".

C'è un modo in cui il codice può essere riscritto per evitare questo avviso? O non esiste un modo kosher di avere oggetti ricorsivi in ​​F #?

risposta

14

Il tuo codice è un modo perfetto per costruire un oggetto ricorsivo. Il compilatore emette un avviso, perché non può garantire che il riferimento non sarà accessibile prima che sia inizializzato (il che causerebbe un errore di runtime). Tuttavia, se si sa che EventHandler non chiama la funzione lambda fornita durante la costruzione (non lo fa), allora si può tranquillamente ignorare l'avviso.

Per dare un esempio in cui l'avviso mostra in realtà un problema, si può provare il seguente codice:

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

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

La classe Evil prende una funzione in un costruttore e lo chiama durante la costruzione. Di conseguenza, il riferimento ricorsivo nella funzione lambda tenta di accedere a e prima che sia impostato su un valore (e si otterrà un errore di runtime). Tuttavia, specialmente quando si lavora con gestori di eventi, questo non è un problema (e si ottiene il messaggio di avviso quando si utilizzano correttamente gli oggetti ricorsivi).

Se si vuole sbarazzarsi del l'avviso, è possibile riscrivere il codice utilizzando esplicite ref i valori e l'utilizzo di null, ma allora sarete nello stesso pericolo di un errore di runtime, ma senza l'avvertimento e con il codice di brutto :

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