web-dev-qa-db-fra.com

Problème comprenant la covariance des covariances avec les génériques en C #

Je ne comprends pas pourquoi le code C # suivant ne compile pas.

Comme vous pouvez le voir, j'ai une méthode générique statique Quelque chose avec un paramètre IEnumerable<T> (et T est contraint d'être une interface IA), et ce paramètre ne peut pas être converti implicitement en IEnumerable<IA>.

Quelle est l'explication? (Je ne cherche pas de solution de contournement, juste pour comprendre pourquoi cela ne fonctionne pas).

public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
    public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
    {
        var bar = foo.ToList();

        // All those calls are legal
        Something2(new List<IA>());
        Something2(new List<IB>());
        Something2(new List<CIA>());
        Something2(new List<CIAD>());
        Something2(new List<CIB>());
        Something2(new List<CIBD>());
        Something2(bar.Cast<IA>());

        // This call is illegal
        Something2(bar);

        return bar;
    }

    private static void Something2(IEnumerable<IA> foo)
    {
    }
}

Erreur je rentre dans la ligne Something2(bar):

Argument 1: impossible de convertir 'System.Collections.Generic.List' en 'System.Collections.Generic.IEnumerable'

116
BenLaz

Le message d'erreur est insuffisamment informatif, et c'est de ma faute. Désolé pour ça.

Le problème que vous rencontrez est une conséquence du fait que la covariance ne fonctionne que sur les types de référence.

Vous dites probablement "mais IA est un type de référence" pour le moment. Oui, ça l'est. Mais vous n'avez pas dit que T est égal à IA. Vous avez dit que T est un type qui implémente IA et n type valeur peut implémenter une interface. Par conséquent, nous ne savons pas si la covariance fonctionnera et nous la refusons.

Si vous voulez que la covariance fonctionne, vous devez indiquer au compilateur que le paramètre type est un type de référence avec la contrainte class ainsi que la contrainte d'interface IA.

Le message d'erreur devrait en réalité indiquer que la conversion n'est pas possible car la covariance nécessite une garantie de type référence-type, car c'est le problème fondamental.

219
Eric Lippert

Je voulais simplement compléter l'excellente réponse d'initié d'Eric avec un exemple de code pour ceux qui ne sont peut-être pas familiarisés avec les contraintes génériques.

Changez la signature de Something comme ceci: La contrainte classdoit venir en premier.

public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA
26
Marcell Toth