En este post vamos a ver una palabra clave dentro de .net que todos sabemos que existe, pero casi nunca la utilizamos.
Y esta es la keyword o palabra clave yield
, la cual fue introducida en C# 2, en este post, veremos qué realiza la palabra clave yield
, cómo utilizarla y por supuesto un pequeño ejemplo.
Índice
1 - Para qué sirve yield en programación
En programación utilizamos yield
para evitar tener que crear colecciones de datos temporales.
Eso sí, cuando utilizamos yield return debemos devolver un IEnumerable<T>
ya que necesitamos utilizar la interfaz IEnumerator
.
Podemos denominar colecciones de datos temporales a aquellas listas que simplemente creamos para reutilizarlas justo después en un bucle foreach o similar.
public IEnumerable<string> FiltrarCochesGetNombres(List<Coche> coches)
{
List<string> nombreCoches = new List<string>();
foreach (Coche coche in coches)
{
if (coche.Marca == MarcaCcohe.Opel)
{
nombreCoches.Add(coche.Modelo);
}
}
return nombreCoches;
}
Observamos que únicamente estamos devolviendo los modelos de la marca Opel.
Sí la lógica de nuestro código dice que vamos a tratar con esa información después y que no la necesitamos agrupada, podemos convertir este método para utilizar yield return
:
public IEnumerable<string> FiltrarCochesGetNombresYield(List<Coche> coches)
{
foreach (Coche coche in coches)
{
if (coche.Marca == MarcaCcohe.Opel)
{
yield return coche.Modelo;
}
}
}
2 - Cómo funciona yield return
He indicado antes que es raro o poco común ver código que contiene métodos con yield
, y esto es porque su ejecución parece “mágica” pero no lo es.
Lo que el operador yield
realiza es pausar la ejecución de la iteración y devolver el valor al método que realiza la llamada para que este continúe con su ejecución y cuando termine volverá al siguiente punto del iterador (el que está en el método con yield return
).
public void Ejemplo()
{
List<Coche> coches = new List<Coche>()
{
new Coche(MarcaCcohe.Audi, "A3"),
new Coche(MarcaCcohe.Audi, "A5"),
new Coche(MarcaCcohe.Opel, "Vectra"),
new Coche(MarcaCcohe.Opel, "Astra"),
};
foreach (string modelo in FiltrarCochesGetNombresYield(coches))
{
Console.WriteLine($"El modelo del cohce es {modelo}");
}
}
nota: cuando utilzamos .Select()
en un comando linq es como si hicieramos yield return
:
public IEnumerable<string> FiltrarCochesGetNombresYield(List<Coche> coches)
{
return coches.Where(a => a.Marca == MarcaCcohe.Opel).Select(a => a.Modelo);
}
Siempre y cuando estemos devolviendo IEnumerable<T>
y no List<T>
.
Lo cual pues siempre ayuda a mejorar el rendimiento, ya sabéis los que me seguís de normal que soy bastante tiquismiquis con el rendimiento.
3 - Ejemplo yield return en c#
Personalmente tuve la fortuna de encontrarme con una pregunta en una entrevista que hice, la cual se resolvió, o bueno, una solución era utilizar yield return
.
El problema era el siguiente:
Implementar una función que recibe por parametro un IEnumerable<T>
y un batch size. había que dividir ese IEnumerable
para devolver IEnumerable<List<T>>
donde cada uno de los List<T>
es igual o menor que el batch size:
public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> enumerable, int batchCount)
{
// Code...
}
Y este fué el resultado final:
public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> enumerable, int batchCount)
{
using (var enumerator = enumerable.GetEnumerator()) //get iteration for the Ienumerable.
{
while (enumerator.MoveNext())
{
//(re)start the values for the loop.
List<T> result = new List<T>();
var position = 0;
do
{
result.Add(enumerator.Current);//add current iteration
position++;
} while (position < batchCount && enumerator.MoveNext());
yield return result;
}
}
}
Conclusión
- Cuando utilizamos yield return lo que hacemos es parar la ejecución del bucle que contiene el yield return para continuar con el método que lo ha llamado.
- Por este motivo yield return nos puede dar mejoras en el rendimiento y el uso de la ram lo cual siempre es importante.
- Podemos utilizar
yield break
para “romper el bucle” y así lo podemos utilizar dentro de un bloquetry{} catch{}
pero no en un finally. - Una vez nos acostumbramos a utilizarlo, podemos ver que es muy útil y muy potente, pero desafortunadamente no es muy común.