Incantation à partir de IEnumerable < Objet > à IEnumerable < string >
-
03-07-2019 - |
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.
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.