2009-06-08 6 views
5

Je ne comprends pas pourquoi le compilateur ne peut pas résoudre la surcharge correcte à utiliser ici. (code ci-dessous) Il y a seulement une version de Add() qui est appropriée - BigFoo est un IFoo, et n'implémente pas IEnumerable où T est un IFoo. Mais il insiste pour signaler une ambiguïté. Des idées? J'ai essayé d'ajouter un deuxième paramètre de type générique - Ajouter où T: IFoo où U: IEnumerable. Mais alors la surcharge est complètement ignorée, même pour un usage légitime. Je sais que je peux contourner ce problème avec le lancement et la spécification des paramètres de type génériques, mais à ce moment-là, j'ai vaincu le but d'avoir une surcharge. Vous pourriez remettre en question la surcharge, mais la sémantique me semble correcte - le comportement que j'applique dans ma classe est à la fois pour Add() d'ajouter l'objet en gros comme une entrée individuelle dans la collection. (Le second Ajouter() n'est pas censé être un AddRange().)Surcharge générique C# - Le compilateur ne peut pas déterminer l'appel correct

namespace NS 
{ 
    interface IFoo { } 

    class BigFoo : IFoo, IEnumerable<int> 
    { 
    public IEnumerator<int> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    } 

    class FooContainer 
    { 
    public void Add(IFoo item) { } 
    public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
    } 

    class DemoClass 
    { 
    void DemoMethod() 
    { 
     BigFoo bigFoo = new BigFoo(); 
     FooContainer fooContainer = new FooContainer(); 
     // error CS0121: The call is ambiguous between the following methods or properties: 
     // 'NS.FooContainer.Add(NS.IFoo)' and 
     // 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)' 
     fooContainer.Add(bigFoo); 
    } 
    } 
} 

Répondre

6

résolution de surcharge générique ne prend pas en compte des contraintes, il juge la version Add<T> soit applicable, inférant T=int.

Les deux méthodes sont applicables, et aucune n'est nettement meilleure que l'autre, car il n'y a pas de conversion entre IEnumerable<int> et IFoo. Alors que les méthodes génériques sont considérées comme «moins spécifiques» que les méthodes non génériques, cela ne devient pertinent que lorsque les types de paramètres sont identiques après le remplacement des arguments de type, ce qui n'est pas le cas dans ce cas.

+0

Jeff Richter accepte "le compilateur C# préfère un match plus explicite sur un modèle générique correspondant" Display ("Jeff") correspondrait à l'affichage (String) Affichage (T) – Gishu

+1

Les règles de bris d'égalité ne s'appliquent pas si les types de paramètres formels sont IDENTICAL. Par exemple, si vous avez M (int x) et M (T t) alors le premier est meilleur que M (int t). –

+0

Ah, merci Eric. Il est bon d'avoir la spécification en ligne et de contribuer;) Éditera de manière appropriée. –

0

Le compilateur doit être suffisamment intelligent pour reconnaître que BigFoo ne peut pas être converti en IEnumerable<IFoo>, mais ce n'est pas le cas. Il voit simplement que c'est un IEnumerable<T>, et estime qu'il s'agit d'un candidat potentiel de surcharge (même si le bloc conts que vous avez défini impose T doit être IFoo et int ne peut pas être converti en IFoo). Bien que ce ne soit pas pratique, ce n'est pas un gros problème. Il suffit de jeter bigFoo à IFoo et le compilateur sera heureux:

fooContainer.Add((IFoo)bigFoo); 

Alternativement, vous pouvez faire de votre surcharge générique Ajouter plus laid:

public void Add<T, U>(U group) 
    where T : IFoo 
    where U : IEnumerable<T> 
{ 
} 

Quoi qu'il en soit, vous avez plus besoin de taper, le second élimine la solution la nécessité de lancer des appels à Add, mais vous devrez déclarer explicitement le type sur les appels à l'ajout générique (qui finit par être plus de code:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo); 
1

Dans FooContainer, sur le deuxième "Add" vous contraindre T à être de type IFoo. BigFoo implémente l'interface IFoo, donc il correspond un peu à cette définition Add (même si elle ne le fait pas vraiment, car elle n'implémente pas IEnumable <IFoo>).

Je ne suis pas sûr que je comprends parfaitement ce que vous voulez, mais je pense qu'il est ceci:

public void Add<T>(T group) where T : IEnumerable<IFoo> { } 

qui vous permettra d'ajouter une T objet où T est un ensemble dénombrable d'objets IFoo.

Est-ce ce que vous vouliez?

Cordialement, Richard

+0

contraint T à IEnumerable au lieu de IFoo n'a toujours pas aidé avec la résolution de surcharge, cependant. Comme l'a dit Jon Skeet, le compilateur (malheureusement) ne prendra pas en compte les contraintes de compte. –

+0

Ah ... Cool. Appris quelque chose (autre) nouveau aujourd'hui. :-) Merci de prendre le temps de commenter - Je pensais vraiment que le compilateur considérerait les contraintes dans la clause "where", même si cela ne dérangeait pas de les considérer ailleurs dans la signature de la méthode. –

0

Le problème est ici que les contraintes de type générique sont complètement ignorés par le compilateur (il ne porte que sur les types de paramètres). En ce qui concerne le compilateur, l'argument IEnumerable<T> étant passé pourrait tout aussi bien être un IEnumerable<IFoo>.

Pour des informations complètes à ce sujet, référez-vous à la section 25.6.4 Inférence des arguments de type du C# Language Specification. Notez qu'il n'y a aucune mention de l'utilisation des contraintes de type.

+0

Ce n'est pas l'endroit approprié pour rechercher les règles de spécification qui sont pertinentes à cette question. La section pertinente dans la spécification 3.0 est 7.5.5.1, le bit qui commence "la validation finale de la meilleure méthode choisie est effectuée". Comme vous pouvez le voir dans cette section, la vérification des violations de contraintes est effectuée APRÈS la vérification de l'unicité de l'ensemble candidat. –

+0

Ah, c'est vrai. Certes, cette section de la spécification suggère au moins que les contraintes de type générique * ne sont pas * prises en compte au cours du processus d'inférence, cependant? – Noldorin

+0

En effet, il est également vrai que les contraintes ne sont pas utilisées lors de l'inférence. –

0

Intéressant .... Juste essayé votre échantillon. Génériques continue de me garder sur mes orteils.

//1 - First preference 
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); } 
//2 - Second Preference 
public void Add<T>(T item) { Console.WriteLine("Generic Add"); } 
//3 - Third preferences 
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); } 
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists 
public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
Questions connexes