81 votes

Pourquoi C # n'infère-t-il pas mes types génériques?

Je vais avoir beaucoup de Funcy plaisir (fun prévu) avec des méthodes génériques. Dans la plupart des cas, C# inférence de type est assez intelligent pour savoir ce générique arguments, il faut l'utiliser sur mes méthodes génériques, mais maintenant, j'ai une conception où le compilateur C# ne réussissez pas, alors que je crois qu'il pourrait avoir réussi à trouver le bon type.

Quelqu'un peut-il me dire si le compilateur est un peu stupide dans ce cas, ou est-il très clairement la raison pourquoi il ne peut pas en déduire mes arguments génériques?

Voici le code:

Catégories et définitions de l'interface:

interface IQuery<TResult> { }

interface IQueryProcessor
{
    TResult Process<TQuery, TResult>(TQuery query)
        where TQuery : IQuery<TResult>;
}

class SomeQuery : IQuery<string>
{
}

Un code qui ne compile pas:

class Test
{
    void Test(IQueryProcessor p)
    {
        var query = new SomeQuery();

        // Does not compile :-(
        p.Process(query);

        // Must explicitly write all arguments
        p.Process<SomeQuery, string>(query);
    }
}

Pourquoi est-ce? Ce qui me manque ici?

Voici le message d'erreur compilateur (il ne doit pas laisser beaucoup de place à notre imagination):

Le type des arguments de la méthode IQueryProcessor.Processus(TQuery) ne peut être déduite à partir de l'utilisation. Essayez de spécifier le les arguments de type explicite.

La raison pour laquelle je crois que C# doit être en mesure d'en déduire qu'il est l'une des raisons suivantes:

  1. J'offre un objet qui implémente IQuery<TResult>.
  2. Que seulement IQuery<TResult> version type implémente est - IQuery<string> et donc TResult doit être string.
  3. Avec cette information, le compilateur a TResult et TQuery.

SOLUTION

Pour moi, la meilleure solution était de changer l' IQueryProcessor interface et l'utilisation typage dynamique dans la mise en œuvre:

public interface IQueryProcessor
{
    TResult Process<TResult>(IQuery<TResult> query);
}

// Implementation
sealed class QueryProcessor : IQueryProcessor {
    private readonly Container container;

    public QueryProcessor(Container container) {
        this.container = container;
    }

    public TResult Process<TResult>(IQuery<TResult> query) {
        var handlerType =
            typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
        dynamic handler = container.GetInstance(handlerType);
        return handler.Handle((dynamic)query);
    }
}

L' IQueryProcessor interface prend maintenant en IQuery<TResult> paramètre. De cette façon, il peut retourner un TResult et cela va résoudre les problèmes du point de vue du consommateur. Nous avons besoin d'utiliser la réflexion dans la mise en œuvre pour obtenir la mise en œuvre effective, depuis le concrètes types de requêtes sont nécessaires (dans mon cas). Mais voici typage dynamique à la rescousse qui va faire la réflexion pour nous. Vous pouvez en lire plus à ce sujet dans cet article.

64voto

Eric Lippert Points 300275

Un tas de gens ont fait remarquer que le C# n'a pas de tirer des conclusions basées sur les contraintes. C'est exact, et un intérêt pour la question. Les inférences sont faites par l'examen des arguments et de leurs correspondants formelle des types de paramètres et qui est la seule source d'inférence de l'information.

Un tas de gens ont alors lié à cet article:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

Cet article est à la fois obsolète et sans intérêt pour la question. Il est out-of-date, car il décrit une décision de conception, nous avons fait en C# 3.0, ce qui nous est ensuite inversé en C# 4.0, principalement basée sur la réponse à cet article. Je viens d'ajouter une mise à jour à cet effet à l'article.

Il n'est pas pertinent parce que l'article est sur le type de retour de l'inférence à partir de la méthode de groupe arguments délégué générique paramètres formels. Ce n'est pas la situation de l'affiche originale sur demande.

L'article pertinent de la mine de lire est plutôt celui-ci:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

16voto

Reed Copsey Points 315315

C # n'inférera pas les types génériques basés sur le type de retour d'une méthode générique, mais uniquement les arguments de la méthode.

Il n'utilise pas non plus les contraintes dans l'inférence de type, ce qui élimine la contrainte générique de fournir le type pour vous.

Pour plus de détails, voir l'article d' Eric Lippert sur le sujet .

11voto

Jon Hanna Points 40291

Il n'utilise pas de contraintes pour déduire des types. Au contraire, il déduit les types (lorsque cela est possible) puis vérifie les contraintes.

Par conséquent, alors que le seul TResult possible pouvant être utilisé avec un paramètre SomeQuery , il ne le verra pas.

Notez également qu'il serait parfaitement possible que SomeQuery implémente également IQuery<int> , ce qui est l'une des raisons pour lesquelles cette limitation du compilateur n'est peut-être pas une mauvaise idée.

5voto

Ed S. Points 70246

La spécification fixe cette très clairement:

Section 7.4.2 L'Inférence De Type

Si le nombre fourni des arguments est différent du nombre de paramètres dans la méthode, alors l'inférence échoue immédiatement. Sinon, supposons que la méthode générique a la signature suivante:

Tr M(T1 x1 ... Tm xm)

Avec un appel de la méthode de la forme M(E1,..., Em), la tâche de l'inférence de type est de trouver unique arguments de type S1...Sn pour chacun des paramètres de type X1...Xn, de sorte que l'appel M(E1,..., Em)devient valide.

Comme vous pouvez le voir, le type de retour n'est pas utilisé pour l'inférence de type. Si l'appel de la méthode n'est pas directement les arguments de type inférence échoue immédiatement.

Le compilateur ne pas simplement supposer que vous vouliez string le TResult d'argument, il ne peut. Imaginez un TResult dérivés de la chaîne. Les deux devraient être valide, donc lequel choisir? Mieux être explicite.

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