67 votes

Pourquoi C# ne peut-il pas déduire le type à partir de ce cas apparemment simple et évident ?

Étant donné ce code :

class C
{
    C()
    {
        Test<string>(A); // fine
        Test((string a) => {}); // fine
        Test((Action<string>)A); // fine

        Test(A); // type arguments cannot be inferred from usage!
    }

    static void Test<T>(Action<T> a) { }

    void A(string _) { }
}

Le compilateur se plaint que Test(A) ne peut pas comprendre T à être string .

Cela me semble être un cas assez facile, et je jure que j'ai fait confiance à des inférences bien plus compliquées dans d'autres fonctions utilitaires et d'extension génériques que j'ai écrites. Qu'est-ce qui me manque ici ?

Mise à jour 1 : Ceci est dans le compilateur C# 4.0. J'ai découvert le problème dans VS2010 et l'échantillon ci-dessus provient d'une reproduction du cas le plus simple que j'ai faite dans LINQPad 4.

Mise à jour 2 : Ajout de quelques exemples supplémentaires à la liste de ce qui fonctionne.

35voto

Jeffrey L Whitledge Points 27574
Test(A);

Cela échoue car la seule méthode applicable ( Test<T>(Action<T>) ) nécessite une inférence de type, et l'algorithme d'inférence de type exige que chaque argument soit d'un certain type ou soit une fonction anonyme. (Ce fait est déduit de la spécification de l'algorithme d'inférence de type (§7.5.2)) La méthode groupe A n'est pas d'un type quelconque (même si elle peut être convertie en un type de délégué approprié), et ce n'est pas une fonction anonyme.

Test<string>(A);

Cette opération réussit, la différence étant que l'inférence de type n'est pas nécessaire pour lier Test, et que le groupe de méthodes A peut être converti dans le type de paramètre de délégué requis. void Action<string>(string) .

Test((string a) => {});

Cela réussit, la différence étant que l'algorithme d'inférence de type prend en compte les fonctions anonymes dans la première phase (§7.5.2.1). Les types de paramètre et de retour de la fonction anonyme sont connus, de sorte qu'une inférence de type de paramètre explicite peut être faite, et une correspondance est ainsi établie entre les types dans la fonction anonyme ( void ?(string) ) et le paramètre de type dans le type de délégué de l'objet Test le paramètre de la méthode ( void Action<T>(T) ). Aucun algorithme n'est spécifié pour les groupes de méthodes qui correspondrait à cet algorithme pour les fonctions anonymes.

Test((Action<string>)A);

Cette opération réussit, la différence étant que le paramètre de groupe de méthodes non typé A est converti en un type, permettant ainsi l'inférence de type de Test pour procéder normalement avec une expression d'un type particulier comme seul argument de la méthode.

Je ne vois pas pourquoi, en théorie, la résolution des surcharges ne pourrait pas être tentée sur le groupe de méthodes. A . Ensuite, si une seule meilleure liaison est trouvée, le groupe de méthodes pourrait recevoir le même traitement qu'une fonction anonyme. Ceci est particulièrement vrai dans des cas comme celui-ci où le groupe de méthodes contient exactement un candidat et n'a pas de paramètres de type. Mais la raison pour laquelle cela ne fonctionne pas dans C#4 semble être le fait que cette fonctionnalité n'a pas été conçue et implémentée. Compte tenu de la complexité de cette fonctionnalité, de la rareté de son application et de l'existence de trois solutions de contournement faciles, je ne vais pas retenir mon souffle pour elle !

8voto

Mehrdad Points 70493

Je pense que c'est parce que c'est une déduction en deux étapes :

  • Il doit en déduire que vous voulez convertir A en un délégué générique.

  • Il doit déduire le type du paramètre du délégué.

Je ne suis pas sûr que ce soit la raison, mais mon intuition est qu'une inférence en deux étapes n'est pas nécessairement facile pour le compilateur.


Edit :

Juste une intuition, mais quelque chose me dit que la première étape est le problème. Le compilateur doit trouver comment convertir en délégué avec un nombre différent de paramètres génériques et ne peut donc pas déduire le type des paramètres.

5voto

AlexD Points 1962

Cela ressemble à un cercle vicieux pour moi.

Test attend un paramètre de type délégué construit à partir du type générique Action<T> . Vous passez dans un groupe de méthodes à la place : Test(A) . Cela signifie que le compilateur doit convertir votre paramètre en un type de délégué ( méthode de conversion de groupe ).

Mais quel type de délégué ? Pour connaître le type du délégué, nous devons connaître T. Nous ne l'avons pas spécifié explicitement, donc le compilateur doit le déduire pour déterminer le type du délégué.

Pour déduire le type des paramètres de la méthode, nous devons connaître les types des arguments de la méthode, dans ce cas le type du délégué. Le compilateur ne connaît pas le type des arguments et échoue donc.

Dans tous les autres cas, l'un ou l'autre type d'argument est apparent :

// delegate is created out of anonymous method,
// no method group conversion needed - compiler knows it's Action<string>
Test((string a) => {});

// type of argument is set explicitly
Test((Action<string>)A); 

ou le paramètre de type est spécifié explicitement :

Test<string>(A); // compiler knows what type of delegate to convert A to

P.S. plus sur l'inférence de type

2voto

Yochai Timmer Points 19802

Vous passez le nom de la Méthode A. Le cadre .Net peut le convertir en un Action mais c'est implicite et il ne veut pas en assumer la responsabilité.

Mais quand même, le nom d'une méthode est PAS un explicite Action<> Objet. Et donc il ne déduira pas le type comme un Action type.

2voto

Thomas Eding Points 8651

Je peux me tromper, mais j'imagine que la vraie raison pour laquelle C# ne peut pas déduire le type est due à la surcharge des méthodes et à l'ambiguïté qui en découle. Par exemple, supposons que j'ai les méthodes suivantes : void foo (int) y void foo (float) . Maintenant, si j'écris var f = foo . Quel foo Le compilateur doit-il choisir ? De même, le même problème se pose avec votre exemple utilisant Test(foo) .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X