Udostępnij za pośrednictwem


Materializacja pośrednia (C#)

Jeśli nie jesteś ostrożny, możesz, w niektórych sytuacjach, znacząco zmienić pamięć i profil wydajności aplikacji, powodując przedwczesne materializacja kolekcji w zapytaniach. Niektóre standardowe operatory zapytań powodują materializację kolekcji źródłowej przed uzyskaniem pojedynczego elementu. Na przykład Enumerable.OrderBy najpierw wykonuje iterację po całej kolekcji źródłowej, a następnie sortuje wszystkie elementy, a następnie zwraca pierwszy element. Oznacza to, że uzyskanie pierwszego elementu uporządkowanej kolekcji jest kosztowne; każdy element nie jest już kosztowny. Ma to sens; nie byłoby możliwe, aby ten operator zapytania zrobił inaczej.

Przykład: Dodawanie metody, która wywołuje ToListmetodę , powodując materializację

W tym przykładzie zmieniono przykład w przykładzie Zapytań łańcuchowych (C#): AppendString metoda została zmieniona na wywołanie ToList przed iteracją za pośrednictwem źródła, co powoduje materializację.

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();
        }
    }
}

Ten przykład generuje następujące wyniki:

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!!!<

W tym przykładzie widać, że wywołanie ToList powoduje AppendString wyliczenie całego źródła przed uzyskaniem pierwszego elementu. Gdyby źródło było dużą tablicą, znacząco zmieniłoby to profil pamięci aplikacji.

Standardowe operatory zapytań można również połączyć ze sobą, jak pokazano w ostatnim artykule w tym samouczku:

Zobacz też