c# - tutorial - Aplanar IEnumerable<IEnumerable<>>; entendiendo genéricos
tutorial linq c# español (2)
Primero, no necesitas Flatten()
; ese método ya existe, y se llama SelectMany()
. Puedes usarlo así:
IEnumerable<IEnumerable<int>> foo = new [] { new[] {1, 2}, new[] {3, 4} };
var bar = foo.SelectMany(x => x); // bar is {1, 2, 3, 4}
Segundo, su primer intento no funciona porque la inferencia de tipo genérico funciona solo en función de los argumentos del método, no de las restricciones genéricas asociadas con el método. Como no hay ningún argumento que use directamente el parámetro genérico J
, el motor de inferencia de tipos no puede adivinar qué J
debería ser, y por lo tanto no cree que su método sea un candidato.
Es edificante ver cómo SelectMany()
esto: requiere un argumento Func<TSource, TResult>
. Eso permite que el motor de inferencia de tipos determine ambos tipos genéricos, ya que ambos están disponibles basándose únicamente en los argumentos proporcionados al método.
Escribí este método de extensión (que compila):
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;
}
El siguiente código causa un error de tiempo de compilación (no se encontró un método adecuado), ¿por qué? :
IEnumerable<IEnumerable<int>> foo = new int[2][];
var bar = foo.Flatten();
Si implemento la extensión como a continuación, no obtengo ningún error de tiempo de compilación:
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;
}
Editar (2) : Esta pregunta que considero que fue respondida, pero generó otra pregunta con respecto a la resolución de sobrecarga y restricciones de tipo Esta pregunta la puse aquí: ¿Por qué las restricciones de tipo no forman parte de la firma del método?
la respuesta de dlev está bien; Solo pensé en añadir un poco más de información.
Específicamente, observo que está intentando usar genéricos para implementar una especie de covarianza en IEnumerable<T>
. En C # 4 y superior, IEnumerable<T>
ya es covariante.
Tu segundo ejemplo ilustra esto. Si usted tiene
List<List<int>> lists = whatever;
foreach(int x in lists.Flatten()) { ... }
luego, la inferencia de tipos hará que la List<List<int>>
sea convertible a IE<List<int>>
, La List<int>
se pueda convertir a IE<int>
, y por lo tanto, debido a la covarianza, IE<List<int>>
es convertible a IE<IE<int>>
. Eso le da a la inferencia de tipo algo para continuar; puede inferir que T es int, y todo es bueno.
Esto no funciona en C # 3. La vida es un poco más difícil en un mundo sin covarianza, pero puedes hacerlo con un uso juicioso del método de extensión Cast<T>
.