2017-10-16 3 views
1

Bonjour, Je viens de commencer à programmer en F # et je suis bloqué sur un problème de type. J'ai cette fonction:Le paramètre a été utilisé de manière à toujours être DerrivedType

member private this.UpdateStats<'T when 'T :> StatisticsBase>(passed: bool, stats: 'T) = 
     //more stuff.. 
     stats 

J'appelle comme ça:

this.UpdateStats<GroupStats>(true, GroupStats(Id = Guid.NewGuid())) 

Le compilateur dit:

enter image description here Le paramètre a été utilisé d'une manière qui limite à toujours être GroupStats. GroupStats hérite de StatisticsBase.

Que dois-je faire pour rendre la fonction utilisable pour toutes les entités qui héritent de StatisticsBase?

types:

[<AllowNullLiteral>] 
type StatisticsBase() = 
    member val Id = String.Empty with get,set 

[<AllowNullLiteral>] 
type GroupStats() = 
    inherit Stats() 

StatisticsBase hérite effectivement d'un type C# et est utilisé dans un dépôt, mais je peux reproduire l'erreur avec le code ci-dessus

+0

Vous devrez montrer plus de code que cela. Je n'obtiens aucun avertissement avec l'implémentation minimale que vous avez décrite. Je soupçonne que le code de 'UpdateStats' n'est pas aussi simple que vous l'avez laissé. –

Répondre

3

Après beaucoup avant et en arrière, nous avons été en mesure de vérifier que votre code non-travail réelle est comme celui-ci (pointe pour l'avenir: fournir plus d'informations):

type SomeType() = 
    member this.M2() = 
     this.M<GroupStats>(true, GroupStats()) 

    member private this.M<'T when 'T :> Stats>(x: bool, t: 'T) = 
     t 

ce code va en effet produire le descr erreur ibed. C'est parce que l'inférence de type F # fonctionne de haut en bas, de gauche à droite. Mais il existe une exception: les classes (et d'autres groupes de définitions mutuellement recusus) obtiennent deux passes d'inférence de type - les premières signatures, puis les corps.

Lorsque le compilateur rencontre pour la première fois le corps de M2, il détermine que la méthode M doit renvoyer GroupStats. Plus tard, lorsque le compilateur rencontre le corps de M, il voit que la valeur de retour M est la même que le paramètre t, ce qui signifie que M doit retourner 'T. Mais puisque le compilateur sait déjà, en examinant le corps de M2, que M doit retourner GroupStats, il s'ensuit que 'T doit être GroupStats.

Cela ne se produit pas si M est défini avant M2: dans ce cas, l'inférence de type va d'abord rencontrer le corps de M et correctement déterminer son type de retour à être 'T, qui sera ensuite correspondre avec le corps de M2, et le sera pas de problème. A partir de ce qui précède, deux solutions peuvent être formulées: d'abord, vous pouvez définir M avant M2. En second lieu, vous pouvez simplement spécifier explicitement le type de retour de M:

member private this.M<'T when 'T :> Stats>(x: bool, t: 'T): 'T = 
     t 

De cette façon, son type de retour sera connu après le premier passage, et le problème disparaît.

+0

Merci, je sais ce que j'ai mal fait. Si je déclare d'abord la fonction UpdateStats puis que j'appelle la fonction au lieu de l'inverse, tout fonctionne correctement.Pouvez-vous ou quelqu'un expliquer pourquoi j'ai eu l'erreur de type quand la séquence était erronée? Je marque votre réponse si vous mettez à jour la réponse avec la séquence. Merci encore! –

+0

J'ai mis à jour la réponse. –