2012-08-16 3 views
2

Le modèle actif de this question ne parvient pas à se compiler après la mise à niveau vers VS 2012 RTM. Il fournit un moyen de faire un test de type et de faire correspondre un littéral dans un seul modèle. Par exemple:Combinaison de types test de type et littéral

let (|Value|_|) value = 
    match box value with 
    | :? 'T as x -> Some x 
    | _ -> None 

let getValue (name: string) (r: IDataReader) = 
    match r.[name] with 
    | null | :? DBNull | Value "" -> Unchecked.defaultof<_> 
    | v -> unbox v 

Est-ce possible sans le modèle actif? Je me rends compte qu'un gardien when pourrait être utilisé (:? string as s when s = "") mais il ne peut pas être combiné avec d'autres modèles.

Répondre

1

variation de KVB (qui ne fait pas tout à fait la même chose car il suppose le test de type réussit) peut être modifié pour produire un motif similaire:

let (|Value|_|) x value = 
    match box value with 
    | :? 'T as y when x = y -> Some() 
    | _ -> None 

Cependant, il y a une différence subtile de performance . Le motif actif d'origine se traduit par:

public static FSharpOption<T> |Value|_|<a, T>(a value) 
{ 
    object obj = value; 
    if (!LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj)) 
    { 
     return null; 
    } 
    return FSharpOption<T>.Some((T)((object)obj)); 
} 

qui est, il fait un essai de type et de fonte. Il est l'utilisation (match x with Value "" -> ...) se traduit par:

FSharpOption<string> fSharpOption = MyModule.|Value|_|<object, string>(obj); 
if (fSharpOption != null && string.Equals(fSharpOption.Value, "")) 
{ 
    ... 
} 

Plus particulièrement, la valeur retournée dactylographié à partir du modèle est mis en correspondance avec les transformations typiques du compilateur pour les modèles (string.Equals pour les chaînes).

Le modèle mis à jour se traduit par:

public static FSharpOption<Unit> |Value|_|<T, a>(T x, a value) 
{ 
    object obj = value; 
    if (LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj)) 
    { 
     T y = (T)((object)obj); 
     T y3 = y; 
     if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<T>(x, y3)) 
     { 
      T y2 = (T)((object)obj); 
      return FSharpOption<Unit>.Some(null); 
     } 
    } 
    return null; 
} 

qui utilise l'égalité générique et est moins efficace que contre un littéral correspondant. L'utilisation est un peu plus simple puisque l'égalité est cuite dans le modèle:

FSharpOption<Unit> fSharpOption = MyModule.|Value|_|<string, object>("", obj); 
if (fSharpOption != null) 
{ 
    ... 
} 

De toute façon, cela fonctionne. Mais j'aime mieux l'original.

+0

Cela fonctionne, mais cela ne vous permet pas non plus d'utiliser des modèles imbriqués (parce que le paramètre du modèle actif paramétré est une expression évaluée, plutôt qu'un modèle comparé). –

+0

Droite. C'est la cause première des différentes sémantiques d'égalité, mais vous l'avez dit de façon plus concise. – Daniel

1

Vous devriez être en mesure d'utiliser un modèle actif paramétrées:

let (|Value|_|) v x = 
    if unbox x = v then 
     Some() 
    else None 

L'utilisation doit ressembler exactement à ce que vous avez maintenant.

Modifier

Bien que je ne sais pas si le changement de rupture était intentionnel, je crois que les modèles actifs avec des types de retour génériques sans rapport avec les types d'entrée doivent généralement être évités. Lorsqu'elles sont combinées avec l'inférence de type, elles peuvent facilement masquer des erreurs subtiles. Prenons l'exemple suivant, en utilisant votre (|Value|_|) modèle original:

match [1] with 
| Value [_] -> "Singleton" 
| _ -> "Huh?" 

Il semble que ce n'est pas quelque chose que vous en fait jamais essayer - le nom implique que Value ne doit être utilisé avec littéraux; Les patterns actifs paramétrés permettent précisément ce scénario.

+0

Votre solution de contournement fait quelque chose d'un peu différent, mais il fournit une bonne direction. Voir ma réponse pour une variante compatible. – Daniel

+0

Bon point. – kvb

+0

Je pense que votre contre-exemple est un peu un homme de paille. '(| Value | _ |)', ou toute fonction générique dans son type de retour, nécessite que vous soyez conscient du type déduit. Votre exemple en dit plus sur '+', à savoir, que ses opérandes sont inférés comme étant 'int', à moins d'être en ligne. – Daniel

Questions connexes