Поделиться через


Промежуточная материализация (C#)

Если вы не осторожны, в некоторых ситуациях резко измените память и профиль производительности приложения, вызвав преждевременную материализацию коллекций в запросах. Некоторые стандартные операторы запросов материализуют свою исходную коллекцию еще до получения хотя бы одного элемента. Например, при выполнении оператора Enumerable.OrderBy вначале происходит итерация по всей исходной коллекции, затем сортировка всех элементов и лишь после этого осуществляется возврат первого элемента. Это означает, что это дорого, чтобы получить первый элемент упорядоченной коллекции; каждый элемент после этого не дорогой. Это имеет смысл; В противном случае для этого оператора запроса было бы невозможно.

Пример. Добавление метода, вызывающего ToListматериализацию

Этот пример изменяет пример в примере запросов цепочки (C#):AppendString метод изменяется на вызов ToList перед итерацией по источнику, что приводит к материализации.

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

В примере получается следующий вывод.

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

В этом примере можно видеть, что применение вызова ToList вынуждает метод AppendString сформировать перечисление по всему источнику, прежде чем возвратить первый элемент. Если источник представляет собой большой массив, то в результате характер использования памяти приложением существенно изменится.

Операторы стандартных запросов также можно объединить в цепочку, как показано в заключительной статье в этом руководстве:

См. также