Question

Récemment, j'ai trouvé un comportement très surprenant dans c #. J'avais une méthode qui prend IEnumerable < Object > en tant que paramètre et que je passais IEnumerable < string > mais ce n'est pas possible. Pendant que c # tout peut être upcast à Object que pourquoi ce n’est pas possible? C'est totalement déroutant pour moi. S'il vous plaît, quelqu'un me clarifie sur cette question.

Était-ce utile?

La solution

Le terme technique utilisé pour cela est que les génériques sont invariants dans C # 3.0 et les versions antérieures. À partir de C 4.0, la distribution fonctionne.

Ce que signifie invariant, c'est qu'il n'y a pas de relation entre deux types génériques simplement parce que leurs paramètres de type générique sont liés (c'est-à-dire qu'ils sont des sous-types ou des supertypes l'un de l'autre).

Dans votre exemple, il n'y a pas de relation de frappe entre un IEnumerable < objet > et un IEnumerable < chaîne > , simplement parce que string est un sous-type d'objet. Ils sont simplement considérés comme deux types complètement indépendants, comme une chaîne et un int (ils sont tous les deux sous-types d'objet, mais tout l'est)

Il existe quelques solutions de contournement et exceptions à ce problème que vous avez rencontré.

Tout d'abord, vous pouvez convertir chaque chaîne individuellement en objet. Si vous utilisez .NET 3.0, vous pouvez le faire à l'aide de la méthode d'extension Diffuser < T > () . Sinon, vous pouvez utiliser un foreach et placer le résultat dans une nouvelle variable du type statique souhaité.

Deuxièmement, les tableaux sont une exception pour le type de référence, c’est-à-dire que le passage d’un type chaîne [] à une méthode qui accepte les types objet [] devrait fonctionner.

Autres conseils

Comme d'autres l'ont souligné, les types de génériques sont invariants. IEnumerable < T > peut être une co-variante, mais C # ne prend pas en charge la spécification de variantes. C # 4.0 devrait prendre en charge les variantes, ce qui pourrait le faire à l'avenir.

Pour contourner ce problème, vous pouvez utiliser la méthode d'extension LINQ Distribution () . En supposant que vous ayez une méthode appelée Foo qui prend un IEnumerable < objet > > . Vous pouvez l'appeler comme ça,

Foo(stringEnumerable.Cast<object>());

Le moyen le plus simple de passer IEnumerable < string > à une fonction nécessitant IEnumerable < objet > consiste à convertir une fonction comme ceci:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
    {
    foreach (T o in enumerable)
        yield return o;
    }

Lorsque le n ° 4 sortira, cela ne sera pas nécessaire, car il supportera la covariance et la contravariance.

Vous devriez utiliser

IEnumerable<T> 

si vous souhaitez transmettre différents types, vous pouvez interroger T pour savoir de quel type il s'agit.

Une bonne façon de penser à cela est de vous demander "Que se passerait-il si vous pouviez le faire?". Prenons l'exemple suivant:

IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal

objects.Add(new Integer(5)); // what the...

Nous venons d'ajouter un entier à une liste de chaînes. Le compilateur agit ainsi pour préserver la sécurité du type.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top