32 votes

Bug du compilateur C# ? Pourquoi cette conversion implicite définie par l'utilisateur ne compile-t-elle pas ?

Étant donné la structure suivante :

public struct Foo<T>
{
   public Foo(T obj) { }

   public static implicit operator Foo<T>(T input)
   {
      return new Foo<T>(input);
   }
}

Ce code se compile :

private Foo<ICloneable> MakeFoo()
{
    string c = "hello";
    return c; // Success: string is ICloneable, ICloneable implicitly converted to Foo<ICloneable>
}

Mais ce code ne compile pas -- pourquoi ?

private Foo<ICloneable> MakeFoo()
{
    ICloneable c = "hello";
    return c; // Error: ICloneable can't be converted to Foo<ICloneable>. WTH?
}

30voto

Philippe Leybaert Points 62715

Apparemment, les conversions implicites définies par l'utilisateur ne fonctionnent pas lorsque l'un des types est une interface. Extrait des spécifications du C# :


6.4.1 Conversions autorisées définies par l'utilisateur

C# ne permet de déclarer que certaines conversions définies par l'utilisateur. En particulier, il n'est pas possible de redéfinir une conversion implicite ou explicite déjà existante. Pour un type source S et un type cible T donnés, si S ou T sont des types nullables, S0 et T0 font référence à leurs types sous-jacents, sinon S0 et T0 sont égaux à S et T respectivement. Une classe ou une structure est autorisée à déclarer une conversion d'un type source S vers un type cible T uniquement si toutes les conditions suivantes sont remplies :

  • S0 et T0 sont des types différents.
  • S0 ou T0 est la classe ou le type de structure dans lequel la déclaration de l'opérateur a lieu.
  • Ni S0 ni T0 n'est un type d'interface. .
  • À l'exception des conversions définies par l'utilisateur, il n'existe pas de conversion de S à T ou de T à S.

Dans votre première méthode, les deux types ne sont pas des types d'interface, donc la conversion implicite définie par l'utilisateur fonctionne.

Les spécifications ne sont pas très claires, mais il me semble que si l'un des types concernés est un type d'interface, le compilateur n'essaie même pas de rechercher les conversions implicites définies par l'utilisateur.

24voto

Eric Lippert Points 300275

(Suite aux commentaires de la réponse acceptée).

Oui, c'est une partie très, très confuse de la spécification. Toute la partie concernant les "types englobants", en particulier, est profondément défectueuse. Cela fait plusieurs années que j'essaie de trouver le temps de réécrire complètement cette section pour en faire quelque chose de plus cohérent, mais cela n'a jamais été une priorité suffisante.

Essentiellement ce que nous avons ici est une contradiction ; nous dites qu'il n'y a pas de conversions implicites définies par l'utilisateur impliquant des interfaces, mais il est clair que ce n'est pas vrai dans ce cas ; il y a une conversion implicite définie par l'utilisateur de IC vers Foo<IC> , démontrée par le fait qu'une chaîne va à Foo<IC> via cette conversion.

Ce que nous devrions vraiment mieux souligner, c'est cette ligne que vous avez citée :

En particulier, il n'est pas possible de redéfinir une conversion implicite ou ou explicite déjà existante.

C'est ce qui motive tout cela : le désir de ne jamais laisser penser que l'on effectue un test de type préservant la représentation alors qu'en fait, on appelle une méthode définie par l'utilisateur. Considérez par exemple cette variation :

interface IBar {}
interface IFoo : IBar {}
class Foo<T> : IFoo
{
   public static explicit operator Foo<T>(T input) { whatever }
}
class Blah : Foo<IBar> {}
...
IBar bar = new Blah();  
Foo<IBar> foo = (Foo<IBar>)bar;

Maintenant, Est-ce que cela appelle la conversion explicite définie par l'utilisateur ou non ? L'objet est réellement dérivé de Foo, on peut donc espérer qu'il ne le soit pas ; il devrait s'agir d'un simple test de type et d'une affectation de référence, et non d'un appel à une méthode d'aide. Un cast sur une valeur d'interface est toujours traité comme un test de type car il est presque toujours possible que l'objet soit réellement de ce type et implémente réellement cette interface. Nous ne voulons pas vous priver de la possibilité d'effectuer une conversion bon marché préservant la représentation.

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