37 votes

S'agit-il d'un bug de covariance dans C# 4 ?

Dans le morceau de code suivant, je m'attendais à pouvoir effectuer un casting implicite à partir de elements à baseElements parce que TBase est implicitement convertible en IBase .

public interface IBase { }
public interface IDerived : IBase { }
public class VarianceBug
{
    public void Foo<TBase>() where TBase : IBase
    {
        IEnumerable<TBase> elements = null;
        IEnumerable<IDerived> derivedElements = null;
        IEnumerable<IBase> baseElements;

        // works fine
        baseElements = derivedElements;

        // error CS0266: Cannot implicitly convert type 
        //   'System.Collections.Generic.IEnumerable<TBase>' to 
        //   'System.Collections.Generic.IEnumerable<IBase>'. 
        //   An explicit conversion exists (are you missing a cast?)
        baseElements = elements;
    }
}

Cependant, je reçois l'erreur mentionnée dans le commentaire.

Citation de la spécification :

Un type T<A1, …, An> est convertible en variance vers un type T<B1, …, Bn> si T est soit une interface, soit un type de délégué déclaré avec les paramètres du type de variante T<X1, …, Xn> et pour chaque paramètre de type de variante Xi l'une des conditions suivantes est remplie :

  • Xi est covariant et une référence implicite ou une conversion d'identité existe de Ai à Bi

  • Xi est contravariant et une référence implicite ou une conversion d'identité existe de Bi à Ai

  • Xi est invariant et une conversion d'identité existe de Ai à Bi

En vérifiant mon code, il semble être conforme à la spécification :

  • IEnumerable<out T> est un type d'interface

  • IEnumerable<out T> est déclaré avec des paramètres de type variante

  • T est covariant

  • une conversion implicite de référence existe de TBase à IBase

Alors - est-ce un bug dans le compilateur C# 4 ?

51voto

Marc Gravell Points 482669

La variance ne fonctionne que pour les types de référence (ou il y a une identité conversion). Il n'est pas connu que TBase est de type référence, sauf si vous ajoutez : class :

 public void Foo<TBase>() where TBase : class, IBase

depuis que j'ai pu écrire un :

public struct Evil : IBase {}

14voto

Bittercoder Points 4692

Marc a raison - j'étais sur le point de coller la même réponse.

Voir la FAQ Covariance & Contravariance :

http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

Extrait de la FAQ :

"La variance est prise en charge uniquement si un paramètre de type est un type de référence".

La variance n'est pas prise en charge pour les types de valeurs

Ce qui suit ne compile pas non plus :

// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.

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