8
votes

Generics: Pourquoi le compilateur ne peut-il pas déduire les arguments de type dans ce cas?

Je voulais écrire une méthode d'extension qui fonctionnerait sur des dictionnaires dont les valeurs étaient une sorte de séquence. Malheureusement, le compilateur ne semble pas sembler déduire les arguments génériques de mon utilisation de la méthode; J'ai besoin de les spécifier explicitement.

public static void SomeMethod<TKey, TUnderlyingValue, TValue>
    (this IDictionary<TKey, TValue> dict)
    where TValue : IEnumerable<TUnderlyingValue> { }    

static void Usage()
{
    var dict = new Dictionary<int, string[]>();
    var dict2 = new Dictionary<int, IEnumerable<string>>();

    //These don't compile
    dict.SomeMethod();
    SomeMethod(dict); // doesn't have anything to do with extension-methods
    dict2.SomeMethod(); // hoped this would be easier to infer but no joy


    //These work fine
    dict.SomeMethod<int, string, string[]>();
    dict2.SomeMethod<int, string, IEnumerable<string>>();
}


3 commentaires

Déduire quejecteurvalue pourrait être difficile. Surtout depuis le paramètre de type de iEnumerable <> est covariant dans .NET 4.


Type Inference est une science exacte qui a des algorithmes formels, ce n'est pas juste une supposition. Si un compilateur n'est pas en mesure de déduire un type, il s'agit simplement du fait qu'il ne dispose pas de règles spécifiques pour gérer certains cas, et c'est généralement une conception de mise en œuvre, pas une limite. Jetez un coup d'œil ici: Classes.cs .uchicago.edu / Archive / 2005 / Hiver / 33600-1 / Diapositives / ...


@Jack: Merci pour le lien; C'est intéressant. Consultez mon commentaire sur la réponse de Eric Lippert pour ce que je voulais dire par ça. Mauvais choix de mots de ma part Je suppose.


3 Réponses :


1
votes

Pourquoi ne pas laisser le type d'iEnumerable?

public static void SomeMethod<TKey, TValue>
(this IDictionary<TKey, TValue> dict)
where TValue : IEnumerable { }    


2 commentaires

Parce que vous ne pourrez donc pas (à court de réflexion) avoir accès au type des valeurs sous-jacentes - tout ce que vous saurez, c'est que chaque valeur est une séquence de quelque chose , mais pas ce que quelque chose est.


Vous pourriez lancer TVALUE à IEnumerable parce que vous savez que c'est du type.



4
votes

L'inférence de type C # ne fonctionne pas de contraintes ni de valeurs de retour. Donc, vous aurez légèrement mieux la chance avec xxx

cela fonctionnera si vous déclarez le paramètre nouveau dictionnaire > ( ) , mais pas si vous le déclarez nouveau dictionnaire > () .

Je dois dire que la façon dont j'ai lu la section 7.5.2 du C # SPEC , il semble que, puisque la liste implémente ienumerable , le type Inférence de TunderlyingValue devrait fonctionner. Cependant, cette section n'est pas parfaitement simple à comprendre. Je suppose qu'il ne fonctionne pas à travers les multiples "couches", puisque SOMEMETHOD (ienumberable Val) {} fonctionnerait bien l'appeler avec SOMEMETHOD (nouvelle liste ()) . Je ne vois rien dans la spécification qui concerne la résolution d'un type où u = ca , donc peut-être que la déduction à ce niveau n'est pas définie.


3 commentaires

+1 @philip rieck: merci. Je suis au courant de cette solution de contournement, mais cela ne résout pas le cas général, ce qui me intéresse.


@Ani Voir les modifications - Je ne suis pas sûr que l'inférence de type définie et mise en œuvre dans C # 4.0 soutient ce niveau d'inférence du tout.


Cela ne fonctionnera pas avec le dictionnaire > car la direction est un type invariant. Supposons que le corps de la méthode contenait ceci: dict.add (nouvelle observablecollection ()); . S'il y avait une interface iReadOnlyDictionnaire d'une sorte, elle pourrait être rendue Covariant sur le paramètre Value et permettre aux deux dictionnaires de fonctionner.



16
votes

Mise à jour: Cette réponse a été écrite il y a plus de dix ans; Depuis lors, la spécification et la mise en œuvre de type d'inférence ont été mises à jour plusieurs fois, notamment les modifications apportées à la manière dont les contraintes sont utilisées lors de la déduction. Cette réponse devrait être considérée comme un intérêt historique uniquement; Consultez une copie récente de la spécification C # pour voir comment l'inférence de type fonctionne dans les implémentations actuelles.

Je réalise que l'inférence de type n'est pas une science exacte

Je ne suis pas sûr d'être d'accord. La spécification est assez détaillée.

Je me demandais s'il y a une «règle» fondamentale, je me manque ici

La règle fondamentale qui vous manque est probablement que les contraintes ne font pas partie de la signature. Type Inference fonctionne de la signature.

Il y a mon avis de bonnes raisons de cette décision de conception. Cependant, beaucoup de gens croient que je suis moralement tort pour croire qu'il y a de bonnes raisons de cette décision de conception. Si vous êtes intéressé par la lecture de ce qui ressemble à plusieurs millions de mots sur le sujet de savoir si j'ai raison ou comme tort, voir mon article sur le sujet et les cent commentaires me disant que je me trompe:

HTTPS : //docs.microsoft.com/en-us/archive/blogs/ericlippert/constraintS-ARE-NOT-Part-Of-Le-Signature

Est-ce une lacune du processus d'inférence?

sans doute, oui. À mon avis, il s'agit d'un choix raisonnable compte tenu des exigences de conception concurrentes. (Ceux qui "font ce que l'utilisateur signifiait" et "donner des erreurs quand les choses ont l'air ambiguë".)

Je m'attends à ce que le compilateur doive "le comprendre" déraisonnable dans ce cas?

Non. Vous semblez être une personne raisonnable et vos attentes semblent être basées sur un bon raisonnement. Cependant, il est tout à fait possible d'avoir une attente raisonnable qui est néanmoins non satisfaite. Ce serait l'un de ces cas.

Puis-je modifier la signature de la méthode d'une manière qui le rendrait aussi fonctionnel que "infâblé"?

Cela va être difficile, car le type de dictionnaire générique n'est pas covariant ni contrevriert dans ses conversions. Le concept que vous souhaitez capturer n'est pas facilement exprimé dans le système de type de manière à infercer.

Si vous préférez utiliser des langues avec une inférence plus avancée de type, envisagez d'utiliser F #. Si vous préférez que les langues visant à "faire ce que l'utilisateur signifiait" plutôt que "signaler des erreurs sur ambiguïté", envisagez d'utiliser VB.


5 commentaires

Brillant, merci - "Les contraintes ne font pas partie de la signature" est ce que j'avais manqué, et je dois envelopper ma tête autour de cela. Sur une autre note, ce que je voulais dire par "non une science exact", c'est qu'il serait possible de deux algorithmes d'inférence différents mais valides (en général, non spécifiques à la spécification) pour déduire les arguments de type différemment, mais les deux sont justifiés dans leur choix. Êtes-vous d'accord?


@Ani: Absolument, il existe de nombreux inférences de type possibles. Nous avons fait un choix délibéré pour ne pas utiliser l'inférence de type de style Hindley-Milner en C #, par exemple.


Quel est le nom de l'inférence de type que vous avez utilisé? Je me demandais simplement si cela a été inventé par quelqu'un d'autre comme l'inférence sur le type de style Hindley-Milner (je suppose).


@Joan: Nous n'avons pas de nom pour cela. C'était un effort conjoint de l'ensemble du comité de design C #. En particulier, Anders, Mads, et j'ai beaucoup travaillé dessus. Erik Meijer et certains des membres de l'équipe de recherche Microsoft ont également contribué de nombreuses idées utiles, en particulier dans le domaine de la détermination des situations cycliques. Je ne me souviens pas de savoir qui tout est sur la déclaration de brevet.


Eric, je ne sais pas si vous remarquerez ce nouveau commentaire, mais je voulais vous demander quelque chose sur le débat sur ce lien que vous avez donné. Une chose que je ressens que les gens ne demandent pas - pourquoi "ni le CLR ni c # considère les contraintes de faire partie de la signature"? Pourquoi ne pas ajouter cela dans la spécification? Avez-vous un lien pour donner la position à ce sujet? Merci.