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:
- J'offre un objet qui implémente
IQuery<TResult>
. - Que seulement
IQuery<TResult>
version type implémente est -IQuery<string>
et donc TResult doit êtrestring
. - 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.