Aplatir IEnumerable<IEnumerable<>>; la compréhension des génériques
J'ai écrit cette méthode d'extension (qui compile):
public static IEnumerable<J> Flatten<T, J>(this IEnumerable<T> @this)
where T : IEnumerable<J>
{
foreach (T t in @this)
foreach (J j in t)
yield return j;
}
Le code ci-dessous provoque une erreur de compilation (pas de méthode appropriée trouvé), pourquoi?:
IEnumerable<IEnumerable<int>> foo = new int[2][];
var bar = foo.Flatten();
Si j'en œuvre de l'extension comme ci-dessous, je n'obtiens pas d'erreur de compilation:
public static IEnumerable<J> Flatten<J>(this IEnumerable<IEnumerable<J>> @this)
{
foreach (IEnumerable<J> js in @this)
foreach (J j in js)
yield return j;
}
Edit(2): Cette question je considère répondu, mais il a soulevé une autre question concernant la résolution de surcharge et le type de contraintes. Cette question je l'ai mis ici: Pourquoi ne pas les contraintes de type partie de la signature de la méthode?
- Votre montage ne fonctionne pas parce que vous avez trop entourant énumérable.
foo.Flatten<IEnumerable<int>, int>();
devrait fonctionner.
Vous devez vous connecter pour publier un commentaire.
Tout d'abord, vous n'avez pas besoin
Flatten()
; cette méthode existe déjà, et est appeléSelectMany()
. Vous pouvez l'utiliser comme ceci:Seconde, votre première tentative ne fonctionne pas parce que l'inférence de type générique fonctionne uniquement basé sur les arguments de la méthode, pas de contraintes génériques associés à la méthode. Depuis il n'y a aucun argument qui utilise directement la
J
paramètre générique, le type de moteur d'inférence ne pouvez pas deviner ce queJ
devrait être, et donc ne pense pas que votre méthode est un candidat.C'est édifiant de voir comment
SelectMany()
obtient cela: il nécessite un supplément deFunc<TSource, TResult>
argument. Qui permet l'inférence de type moteur de déterminer à la fois les types génériques, puisqu'ils sont tous deux basés exclusivement sur des arguments fournis à la méthode.Flatten<IEnumerable<int>,int>(foo)
Flatten
méthode pour divers projets au cours des dernières années, et j'ai toujours appelé çaFlatten
ainsi. Même maintenant que je sais qu'il existe, je pense queSelectMany
est incroyablement trompeur pour le choix du nom de cette méthode, car elle est assez contre-intuitif de supposer que quelque chose appeléSelectMany
serait aplatir une hiérarchie. +1 pour souligner que dans la réalité.dlev la réponse est très bien; j'ai juste pensé que je pourrais ajouter un peu plus d'informations.
Plus précisément, je remarque que vous essayez d'utiliser des génériques de mettre en œuvre une sorte de covariance sur
IEnumerable<T>
. En C# 4 et au-dessus,IEnumerable<T>
déjà est covariant.Votre deuxième exemple illustre bien ce point. Si vous avez
alors l'inférence de type auront raison que
List<List<int>>
convertibleIE<List<int>>
,List<int>
convertibleIE<int>
, et par conséquent, en raison de la covariance,IE<List<int>>
convertibleIE<IE<int>>
. Qui donne l'inférence de type quelque chose à faire; on peut en déduire que T est de type int, et tout est bon.Cela ne fonctionne pas en C# 3. La vie est un peu plus difficile dans un monde sans covariance, mais vous pouvez vous en tirer avec une utilisation judicieuse de la
Cast<T>
méthode d'extension.