¿Para qué se utiliza la palabra clave de rendimiento en C#?
En la pregunta ¿Cómo puedo exponer solo un fragmento de IList<>?, una de las respuestas tenía el siguiente fragmento de código:
IEnumerable<object> FilteredList()
{
foreach(object item in FullList)
{
if(IsItemInPartialList(item))
yield return item;
}
}
¿Qué hace la palabra clave de rendimiento allí? He visto referencias a él en un par de lugares y en otra pregunta, pero no he descubierto muy bien qué hace realmente. Estoy acostumbrado a pensar en el rendimiento en el sentido de que un hilo cede ante otro, pero eso no parece relevante aquí.
La yield
palabra clave contextual realmente hace mucho aquí.
La función devuelve un objeto que implementa la IEnumerable<object>
interfaz. Si una función que llama comienza foreach
a funcionar sobre este objeto, la función se llama nuevamente hasta que "cede". Este es el azúcar sintáctico introducido en C# 2.0 . En versiones anteriores tenías que crear tus propios IEnumerable
objetos IEnumerator
para hacer cosas como esta.
La forma más sencilla de entender un código como este es escribir un ejemplo, establecer algunos puntos de interrupción y ver qué sucede. Intente seguir este ejemplo:
public void Consumer()
{
foreach(int i in Integers())
{
Console.WriteLine(i.ToString());
}
}
public IEnumerable<int> Integers()
{
yield return 1;
yield return 2;
yield return 4;
yield return 8;
yield return 16;
yield return 16777216;
}
Cuando siga el ejemplo, encontrará la primera llamada a Integers()
return 1
. La segunda llamada regresa 2
y la línea yield return 1
no se vuelve a ejecutar.
Aquí hay un ejemplo de la vida real:
public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
using (var connection = CreateConnection())
{
using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
{
command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return make(reader);
}
}
}
}
}
Iteración. Crea una máquina de estado "bajo las sábanas" que recuerda dónde estaba en cada ciclo adicional de la función y continúa desde allí.
El rendimiento tiene dos grandes usos,
Ayuda a proporcionar iteraciones personalizadas sin crear colecciones temporales.
Ayuda a realizar iteraciones con estado.
Para explicar los dos puntos anteriores de manera más demostrativa, he creado un video simple que puedes verlo aquí.