It appears to me as though there is a bug/inconsistency in the C# compiler.
Il existe certainement des bogues et des incohérences dans le compilateur. Vous n'en avez pas trouvé. Le compilateur se comporte complètement correctement et selon les spécifications dans tous ces cas. Je fais de mon mieux pour donner un sens à cette question très confuse. Laissez-moi essayer de décomposer en une série de questions.
Why does this succeed and call the first method?
public void SomeMethod(string message, object data);
public void SomeMethod(string message, params object[] data);
// ....
SomeMethod("woohoo", item);
(Présomption: cet élément est une expression d'un type de compilation objet autre que [].)
résolution de surcharge doit choisir entre deux méthodes applicables. La deuxième méthode n'est applicable que sous sa forme étendue. Une méthode applicable uniquement dans sa forme développée est automatiquement pire qu'une méthode applicable dans sa forme normale. Par conséquent, la meilleure méthode restante est choisie.
Why does this fail with an ambiguity error?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (T)item);
Il est impossible de dire parce que vous ne dites pas ce que "T" est. D'où vient T dans cet exemple? Il y a deux types de paramètres nommés T déclarés; est ce code dans le contexte d'une de ces méthodes? Comme ce sont différents types tous les deux nommés T cela pourrait faire une différence. Ou est-ce encore un troisième type appelé T?
Puisque la question n'a pas assez d'information pour y répondre, je vais poser une meilleure question en votre nom.
Why does this fail with an ambiguity error?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", "hello");
Il ne fonctionne pas. Ça réussit. L'inférence de type choisit "chaîne" pour T dans les deux méthodes. Les deux méthodes génériques sont applicables; le second est applicable dans sa forme élargie, de sorte qu'il perd.
OK, then why does this fail with an ambiguity error?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", null);
Il ne fonctionne pas. Il échoue avec une erreur "can not infer T". Il n'y a pas assez d'informations ici pour déterminer ce que T est dans les deux cas.Comme l'inférence de type ne parvient pas à trouver une méthode candidate, l'ensemble candidat est vide et la résolution de surcharge n'a rien à choisir.
So this succeeds because... ?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (string)null);
inférence de type infère que les deux méthodes sont candidats lorsqu'ils sont construits avec "string". Encore une fois, la deuxième méthode est pire car elle n'est applicable que sous sa forme élargie.
What if we take type inference out of the picture? Why is this ambiguous?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<string>("woohoo", null);
Nous avons maintenant trois candidats applicables. La première méthode, la deuxième méthode dans sa forme normale, et la deuxième méthode dans sa forme développée. La forme développée est ignorée car étendue est pire que la normale. Cela laisse deux méthodes dans leur forme normale, l'une prenant la chaîne et l'autre prenant la chaîne []. Ce qui est mieux?
Face à ce choix, nous choisissons toujours celui qui a le type le plus spécifique. Si vous avez dit
public void M(string s) { ... }
public void M(object s) { ... }
...
M(null);
que nous avions choisi la version de chaîne parce que la chaîne est plus spécifique que l'objet. Chaque chaîne est un objet mais pas tous les objets sont des chaînes.
chaîne n'est pas convertible en chaîne []. string [] n'est pas convertible en chaîne. Aucun n'est plus spécifique que l'autre. Par conséquent, ceci est une erreur d'ambiguïté; il y a deux "meilleurs" candidats.
Then why does this succeed and what does it do?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<object>("woohoo", null);
Encore une fois nous avons trois candidats, pas deux. Nous éliminons automatiquement la forme développée comme avant, en laissant deux. Des deux méthodes en forme normale qui restent, ce qui est mieux?
Nous devons déterminer lequel est le plus spécifique. Chaque tableau d'objets est un objet, mais chaque objet n'est pas un tableau d'objets. object [] est plus spécifique que object, nous choisissons donc d'appeler la version qui prend un objet []. Nous passons un tableau de paramètres null, qui est presque certainement pas ce que vous vouliez.
C'est pourquoi il est extrêmement difficile de programmer des surcharges comme vous le faites. Vous présentez le potentiel pour vos utilisateurs de tomber dans toutes sortes d'ambiguïtés fous quand vous faites ce genre de choses. S'il vous plaît ne pas concevoir des méthodes similaires.
Une meilleure façon de concevoir ce genre de logique est la suivante: (Notez que je ne l'ai pas fait compilé ce code, ceci est juste à côté du haut de ma tête.)
static string ToString<T>(T t)
{
return t == null ? "" : t.ToString();
}
static string ToString<T>(T t1, T t2)
{
return ToString<T>(t1) + ToString<T>(t2);
}
static string ToString<T>(T t1, T t2, params T[] rest)
{
string firstTwo = ToString<T>(t1, t2);
if (rest == null) return firstTwo;
var sb = new StringBuilder();
sb.Append(firstTwo);
foreach(T t in rest)
sb.Append(ToString<T>(t));
return sb.ToString();
}
Maintenant, chaque cas est traité avec une sémantique raisonnable et une efficacité décente. Et pour tout site d'appel donné, vous pouvez immédiatement prédire avec précision quelle méthode sera appelée; il n'y a que trois possibilités: un argument, deux arguments ou plus de deux arguments. Chacun est géré sans ambiguïté par une méthode particulière.
est l'article null? : s –
Veuillez poster un programme court mais complet démontrant le problème, et dites-nous quelle version de C# vous utilisez. –
Comment ça marche si vous ne jetez pas d'objet dans T? Si, à un moment donné, vous aviez déclaré un élément en tant que type T, cela fonctionne-t-il comme prévu? –