14 votes

opérateur implicite avec générique ne fonctionne pas pour l'interface

J'ai essentiellement la classe suivante (exemple trouvé sur C# crée une conversion implicite pour une classe générique ? ).

class MyClass<T>
{
  public MyClass(T val)
  {
     Value = val;
  }

  public T Value { get; set; }

  public static implicit operator MyClass<T>(T someValue)
  {
     return new MyClass<T>(someValue);
  }

  public static implicit operator T(MyClass<T> myClassInstance)
  {
     return myClassInstance.Value;
  }
}

On pourrait faire

MyClass<IFoo> foo1 = new Foo();
MyClass<Foo>  foo2 = new Foo();

//But not
MyClass<IFoo> foo3 = (IFoo)new Foo();

Le vrai problème se pose quand on essaie de faire quelque chose comme

void Bar(IFoo foo)
{
    Bar2(foo);
    //What should be the same as
    Bar2<IFoo>(new MyClass<IFoo>(foo));
}

void Bar2<T>(MyClass<T> myClass)
{
     //Do stuff
}

Comment pourrais-je remanier MyClass pour qu'il soit possible de travailler avec des objets dont on ne connaît que l'interface ?

26voto

Eric Lippert Points 300275

Réponse courte :

Les conversions implicites définies par l'utilisateur ne fonctionnent pas sur les interfaces. N'essayez pas de les faire fonctionner. Trouvez une autre solution à votre problème de système de types.

Longue réponse :

Il s'agit d'une décision délibérée de l'équipe de conception du C#. Le principe est le suivant : lorsque vous effectuez une conversion impliquant une interface, vous souhaitez préserver l'identité référentielle ; vous vous renseignez sur l'interface identité de l'objet qui implémente l'interface, et non pas d'essayer de créer un objet similaire ayant des propriétés semblables.

Le principe général ici est qu'une conversion définie par l'utilisateur ne doit pas remplacer une conversion intégrée. Mais comme presque toutes les classes peuvent être sous-classées, et que cette sous-classe peut implémenter à peu près n'importe quelle interface, il est très difficile de savoir ce qu'est une conversion définie par l'utilisateur. statiquement si une conversion donnée, définie par l'utilisateur et impliquant une interface, peut remplacer une conversion intégrée.

Pour votre information, il s'agit d'une partie particulièrement délicate de la spécification, et le compilateur C# présente quelques bogues à ce niveau. Je soupçonne que l'un de vos cas ci-dessus tire parti de ces bogues ; le fait qu'il existe des programmes réels qui le font est ce qui m'a empêché de corriger les bogues.

Les bogues sont principalement la conséquence du fait que cette fonctionnalité a été conçue avant les génériques, puis n'a pas été suffisamment repensée après que les génériques aient introduit de nombreuses complications imprévues.

Pour plus de détails, voir mes commentaires détaillés ici, en particulier les parties marquées DELIBERATE SPEC VIOLATION qui décrivent les problèmes avec les conversions d'interface.

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs

Comme vous pouvez le constater, ce fichier compte moins de mille lignes, et probablement plus de la moitié de commentaires. Il a fallu semaines Il a fallu des recherches minutieuses et de nombreuses discussions avec l'équipe linguistique pour régler cette sémantique. Une fois que vous avez commis une erreur dans un compilateur, vous devez souvent la comprendre en profondeur une décennie plus tard, puis l'inscrire pour toujours afin de ne pas briser un client lors d'une mise à niveau. Les concepteurs de langage peuvent tirer de nombreuses leçons de la façon dont le C# s'est trompé dans cette partie obscure de la spécification.

Comment pourrais-je remanier MyClass pour qu'il soit possible de travailler avec des objets dont on ne connaît que l'interface ?

N'essayez pas. Transformez la référence de l'interface en réel type d'exécution et de travailler avec elle à partir de là. Ou créez une instance du type souhaité de manière explicite, plutôt que par conversion implicite. N'essayez pas de jouer avec les conversions implicites et les interfaces ; cela ne fonctionnera pas bien.

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