Bogue apparent du compilateur param # C (C # 5.0)

Ceci est un suivi sur un fil que je pensais résolu hier. Hier, j’avais des problèmes avec mon code dans le cas suivant:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication3 { class Program { class Bar { int v; public Bar(int v) { this.v = v; } public override ssortingng ToSsortingng() { return v.ToSsortingng(); } } static void Main(ssortingng[] args) { Foo(1, 2, 3); Foo(new int[] { 1, 2, 3 }); Foo(new Bar(1), new Bar(2), new Bar(3)); Foo(new Bar[] { new Bar(1), new Bar(2), new Bar(3) }); System.Threading.Thread.Sleep(20000); } static void Foo(params object[] objs) { Console.WriteLine("New call to Foo: "); foreach(object o in objs) Console.WriteLine("Type = " + o.GetType() + ", value = "+o.ToSsortingng()); } } } 

Si vous exécutez ceci, vous pouvez voir un problème lors du dernier appel à Foo. Le fait que l’argument soit un vecteur est “perdu”.

Alors …. Quelqu’un sait comment signaler un bogue de compilateur C #? Ou serait-ce considéré comme un bug de reflection?

(Quel soulagement: j’étais stupéfait de penser que j’avais perdu mon temps ici avec un bogue personnel. En fait, c’est un bogue C # après tout, et je suis convaincu! Et combien de fois avons-nous souvent la chance de voir de vrais bogues du compilateur C # ces jours-ci? Pas commun …)

Je m’attendrais à ce que ces deux appels fonctionnent de manière identique – un argument params est un tableau de la méthode appelée. L’exemple de Jon Skeet à la question précédente fonctionne car un tableau d’int est non covariant à un tableau d’objects (et est donc traité comme un new Object[] { new Int[] {1,2,3} } ), mais dans cet exemple un tableau de FooBars est covariant à un tableau d’objects et votre paramètre est donc développé dans l’argument objs .

Wikipedia de toutes choses couvre ce cas précis: Covariance et contravariance (informatique)

Désolé mais je suis sûr que ce n’est pas un bug du compilateur.

MODIFIER:

Vous pouvez réaliser ce que vous voulez ainsi:

 Foo(new Object[] { new Bar[] { new Bar(1), new Bar(2), new Bar(3) } }); 

NEW EDIT (autre auteur):

Ou simplement utiliser:

 Foo((Object)new Bar[] { new Bar(1), new Bar(2), new Bar(3) }); 

La spécification C # 4.0 est assez explicite dans la façon dont tout cela se joue. 7.5.3.1 dit que si une fonction avec params peut être appliquée sous forme normale (en ignorant le mot clé params ) ou sous forme développée (en utilisant le mot clé params ), la forme normale l’emporte.

En supposant que Foo soit déclaré comme Foo(params object[] args) , puis l’appel Foo(new Foobar[] {new Foobar(1), new Foobar(2), new Foobar(3)) }); est applicable sous forme normale, étant donné que Foobar[] est implicitement convertible en object[] (6.1.6, clause 5). Par conséquent, la forme normale est utilisée et la forme développée est ignorée.

(Je suppose que C 5.0 n’a pas changé cette partie du langage.)

Voir la section 7.5.3.1 de la spécification C #:

7.5.3.1 Membre de fonction applicable

Un membre de fonction est dit être un membre de fonction applicable en ce qui concerne une liste d’arguments A lorsque toutes les conditions suivantes sont vraies:

  • Chaque argument de A correspond à un paramètre de la déclaration de membre de fonction décrit au § 7.5.1.1, et tout paramètre auquel aucun argument ne correspond est un paramètre facultatif.
  • Pour chaque argument dans A, le mode de passage de paramètre de l’argument (c’est-à-dire, valeur, ref ou out) est identique au mode de passage de paramètre du paramètre correspondant, et
    • pour un paramètre de valeur ou un tableau de parameters, il existe une conversion implicite (§6.1) de l’argument au type du paramètre correspondant, ou
    • [… certains éléments non pertinents concernant out parameters ref et out …]

Pour un membre de fonction qui inclut un tableau de parameters, si le membre de fonction est applicable selon les règles ci-dessus, il est dit applicable dans sa forme normale. Si un membre de la fonction qui inclut un tableau de parameters n’est pas applicable dans sa forme normale, le membre de la fonction peut plutôt être applicable dans sa forme développée [.]

Parce que le tableau que vous avez passé peut être implicitement converti en object[] , et parce que la résolution de surcharge préfère le formulaire “normal” au formulaire “développé”, le comportement observé est conforme à la spécification et il n’y a pas de bogue.

En plus de la solution de contournement décrite par Chris Shain, vous pouvez également changer Bar d’une classe à une structure; cela rend le tableau non plus implicitement convertible en object[] , vous aurez donc le comportement que vous désirez.

Je pense que vous vous trompez. Ken C’est parce que int [] n’est pas de type object []. Le compilateur suppose donc que int [] n’est qu’un des arguments passés à la méthode.

Voici comment:

 new Foobar[] { } is object[]; // true new int[] { } is object[]; // false 

update: vous pouvez rendre la méthode générique pour que le compilateur sache que le type struct / object est transmis sous forme de parameters:

 void Foo(params T[] objs) { foreach (T o in objs) Console.WriteLine(o.GetType()); } 

Donc, je vais suggérer que la meilleure réponse est que j’ai raison et qu’il s’agit vraiment d’un bug du compilateur. Bien que je comprenne très bien le problème, votre explication ignore le mot clé “params”. En fait, pour utiliser la covariance de cette manière, il FAUT ignorer le mot-clé params, comme s’il n’était pas important. Je postule qu’il n’y a tout simplement aucune explication plausible pour comstackr le code de cette manière avec Params présent en tant que mot clé sur la signature de type de Foo: pour invoquer votre explication, vous devez me convaincre que myClass [] doit correspondre à l’object []. , mais nous ne devrions même pas poser cette question étant donné la structure params. En fait, plus vous y réfléchissez, plus il est clair qu’il s’agit en fait d’un véritable bogue du compilateur C # 5.0: le compilateur néglige d’appliquer le mot clé params. Les spécifications de langue n’ont en réalité besoin d’aucun changement. Je devrais avoir une sorte de badge étrange, à mon humble avis!