2011-12-26 7 views
7

Cet extrait de code F #Objets récursifs en 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 

résultats dans l'avertissement suivant:

traynote.fs (62,41): FS0040 Avertissement: Ceci et d'autres références récursives à l'objet (s) étant défini sera vérifié pour l'initialisation et la fiabilité à l'exécution à l'aide d'une référence retardée. C'est parce que vous définissez un ou plusieurs objets récursifs, plutôt que des fonctions récursives. Cet avertissement peut être supprimé en utilisant '#nowarn' 40 "'ou' --nowarn: 40 '.

Existe-t-il un moyen de réécrire le code pour éviter cet avertissement? Ou n'y a-t-il pas de façon casher d'avoir des objets récursifs dans F #?

Répondre

14

Votre code est une façon parfaite de construire un objet récursif. Le compilateur émet un avertissement, car il ne peut pas garantir que la référence ne sera pas accessible avant son initialisation (ce qui provoquerait une erreur d'exécution). Cependant, si vous savez que EventHandler n'appelle pas la fonction lambda fournie pendant la construction (ce n'est pas le cas), vous pouvez ignorer l'avertissement en toute sécurité.

Pour donner un exemple où l'avertissement montre effectivement un problème, vous pouvez essayer le code suivant:

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 prend une fonction dans un constructeur et appelle lors de la construction. Par conséquent, la référence récursive de la fonction lambda tente d'accéder à e avant qu'elle ne soit définie sur une valeur (et vous obtiendrez une erreur d'exécution). Cependant, en particulier lorsque vous travaillez avec des gestionnaires d'événements, ce n'est pas un problème (et vous obtenez l'avertissement lorsque vous utilisez correctement les objets récursifs).

Si vous voulez vous débarrasser de l'avertissement, vous pouvez réécrire le code en utilisant ref explicites valeurs et à l'aide null, mais vous serez dans le même danger d'une erreur d'exécution, juste sans avertissement et avec le code plus laid :

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