Взаимодействие между урожайностью и LINQ - PullRequest
1 голос
/ 06 августа 2011

Я читал кусок кода из библиотеки «XStreamingReader» (который кажется действительно классным решением для возможности выполнения запросов LINQ над документами XML, но без загрузки фактического документа в память (как в объекте XDocument)и задавался вопросом о следующем:

public IEnumerable<XElement> Elements()
{
    using (var reader = readerFactory())
    {
        reader.MoveToContent();
        MoveToNextElement(reader);
        while (!reader.EOF)
        {
            yield return XElement.Load(reader.ReadSubtree());
            MoveToNextFollowing(reader);
        }
    }
}

public IEnumerable<XElement> Elements(XName name)
{
    return Elements().Where(x => x.Name == name);
}

Относительно второго метода Elements(XName) - Метод сначала вызывает Elements (), а затем использует Where() для фильтрации результатов, но я заинтриговано порядке выполнения здесь, так как Elements () содержит оператор yield. Из того, что я понимаю: - Выполнение Elements () возвращает коллекцию IEnumerable, эта коллекция физически не содержит никаких элементов YET. - Где () выполняется в этой коллекциипозади сцены есть цикл, который перебирает каждый элемент, новые элементы «загружаются» на лету, так как используется yield. - Все элементы, которые соответствуют выражению Where, возвращаются как коллекция IEnumerable и физически находятся в этой коллекции..

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

Ответы [ 3 ]

2 голосов
/ 06 августа 2011

Я предполагаю, что когда вы говорите, что предметы физически находятся в коллекции, вы имеете в виду, что в памяти есть структура, которая содержит все элементы прямо сейчас . С Where() это не так, он использует yield слишком внутренне (или что-то, что действует так же, как yield).

Когда вы пытаетесь получить первый элемент, Where() повторяет исходную коллекцию, пока не найдет первый соответствующий элемент. Таким образом, элементы передаются как в Elements(), так и в Elements(XName), и вся коллекция никогда не находится в памяти, только по частям.

1 голос
/ 06 августа 2011

Где () выполняется в этой коллекции? Во-первых, я прав с вышеуказанным предположением?

Нет.Куда возвращает ленивый IEnumerable<XElement>.Позже, когда это IEnumerable<XElement> будет перечислено, элементы получаются и фильтруются.

Если вещь, которая перечисляет ленивый IEnumerable, собирает элементы (например, вызов ToList), то все элементыбудет в памяти в этот момент.Если то, что перечисляет этот ленивый IEnumerable, обрабатывает каждый элемент по одному (например, цикл foreach, который не сохраняет ссылку на XElement), то в памяти будет только один элемент за раз.

0 голосов
/ 06 августа 2011

Все элементы, которые соответствуют оператору Where, возвращаются как коллекция IEnumerable и находятся ФИЗИЧЕСКИ в этой коллекции.Во-первых, я прав с приведенным выше предположением?

Нет.Where реализует внутри себя дополнительный перечислитель, который делает то, что вы от него хотите.Если IEnumerable не перечислено, то читатель никогда не вызывается, и отдельные экземпляры XElement никогда не создаются, а код фильтрации никогда не запускается.

См. Статью Джона Скита о повторной реализацииповедение предложения Where: http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx.Он имитирует существующую реализацию (в пояснительных целях - нет необходимости использовать его повторную реализацию в реальном коде), и его код использует yield return.

Обратите внимание, что если вы вызываете ToList, товсе перечисление будет оценено и скопировано в список, поэтому будьте осторожны с тем, что вы делаете с IEnumerable, который возвращает Where.

Также имейте в виду, что если читатель вернулсяreaderFactory читает из памяти (например, StringReader), тогда документ будет физически существовать в памяти - экземпляров DOM-узлов просто не будет, пока вы их не перечислите.И как только вы перечислите эти элементы, ваш документ будет существовать дважды в памяти, один для исходного документа, один в форме DOM.Возможно, вы захотите убедиться, что потоковая передача выполняется для потока, не относящегося к памяти (например, непосредственно из файла или сетевого потока).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...