Matérialisation intermédiaire (C#)
Si vous n’y prenez pas garde, dans certaines situations vous risquez de modifier de manière significative le profil de mémoire et de performances de votre application en provoquant la matérialisation prématurée de collections dans vos requêtes. Certains opérateurs de requête standard provoquent la matérialisation de leur collection source avant de générer un seul élément. Par exemple, Enumerable.OrderBy itère au sein de l'ensemble de sa collection source, trie tous les éléments, puis génère le premier élément. Cela signifie qu'il est coûteux d'obtenir le premier élément d'une collection ordonnée ; chaque élément ultérieur présente un faible coût. Tout cela est logique : il est impossible pour cet opérateur de requête de procéder autrement.
Exemple : ajouter une méthode qui appelle ToList
, provoquant la matérialisation
Cet exemple modifie l’exemple dans l’exemple de requêtes de chaîne (C#) : la méthode AppendString
est modifiée pour être appelée ToList avant d’itérer dans la source, entraînant la matérialisation.
public static class LocalExtensions
{
public static IEnumerable<string>
ConvertCollectionToUpperCase(this IEnumerable<string> source)
{
foreach (string str in source)
{
Console.WriteLine("ToUpper: source >{0}<", str);
yield return str.ToUpper();
}
}
public static IEnumerable<string>
AppendString(this IEnumerable<string> source, string stringToAppend)
{
// the following statement materializes the source collection in a List<T>
// before iterating through it
foreach (string str in source.ToList())
{
Console.WriteLine("AppendString: source >{0}<", str);
yield return str + stringToAppend;
}
}
}
class Program
{
static void Main(string[] args)
{
string[] stringArray = { "abc", "def", "ghi" };
IEnumerable<string> q1 =
from s in stringArray.ConvertCollectionToUpperCase()
select s;
IEnumerable<string> q2 =
from s in q1.AppendString("!!!")
select s;
foreach (string str in q2)
{
Console.WriteLine("Main: str >{0}<", str);
Console.WriteLine();
}
}
}
Cet exemple produit la sortie suivante :
ToUpper: source >abc<
ToUpper: source >def<
ToUpper: source >ghi<
AppendString: source >ABC<
Main: str >ABC!!!<
AppendString: source >DEF<
Main: str >DEF!!!<
AppendString: source >GHI<
Main: str >GHI!!!<
Dans cet exemple, on observe que l'appel de ToList force AppendString
à parcourir l'ensemble de sa source avant de générer le premier élément. Si la source est un grand tableau, cela modifie considérablement le profil de mémoire de l'application.
Les opérateurs de requête standard peuvent également être chaînés comme indiqué dans l’article final de ce tutoriel :