Когда использовать LINQ .ToList() или .ToArray()

После запуска этого кода:

var input = new List<T>( ... );
var result = input.Select( t => new U(t) );

U first1 = null;
foreach ( U u1 in result )
    if ( first1 == null )
        first1 = u1;

U first2 = null;
foreach ( U u2 in result )
    if ( first2 == null )
        first2 = u2;

Затем 'first1 == first2' оценивается как false, хотя оба U обертывают один и тот же T. Я еще не проверял это, но я думаю, что его можно заставить оценить как true, объединив .ToList() или .ToArray() на вызов Select().

В реальном коде, который намного сложнее, чем эта простая иллюстрация, какое хорошее эмпирическое правило следует использовать для принятия решения о том, следует ли добавлять .ToList() или .ToArray()? Мои первоначальные мысли — либо любое выражение, на которое ссылаются, которое может повторяться более одного раза, либо, чтобы быть более безопасным, в случае, если потенциальные итерации не очевидны, любое выражение, на которое ссылаются, результат которого никогда не изменится.


person HappyNomad    schedule 18.08.2012    source источник
comment
Вы уверены, что first1 == first2 ложно? Повторение одной и той же коллекции должно каждый раз возвращать одни и те же объекты.   -  person Peter Gluck    schedule 18.08.2012
comment
Да, я уверен. Это то же выражение, но не та же коллекция.   -  person HappyNomad    schedule 18.08.2012
comment
@PeterGluck Это неверно, потому что U использует стандартное равенство ссылок. Каждая итерация вызывает переоценку последовательности, поэтому вы получаете новые U экземпляра.   -  person Reed Copsey    schedule 18.08.2012
comment
возможный дубликат Лучше ли вызывать ToList( ) или ToArray() в запросах LINQ?   -  person nawfal    schedule 11.11.2013
comment
Проблема в том, что вы создаете new U каждый раз, когда перебираете коллекцию? Или тот новый U(t) != new U(t)? У лени есть последствия. Желательны эти последствия или нет, зависит от варианта использования.   -  person Martijn    schedule 01.05.2015


Ответы (2)


К сожалению, я не думаю, что здесь есть хорошее правило «жестко и быстро». Это во многом зависит от того, как вы ожидаете использовать результаты и что на самом деле делает сам запрос.

Сначала я думаю о любом выражении, которое может быть повторено более одного раза, или, чтобы быть более безопасным в случае, если возможные повторения не очевидны, о любом выражении, результат которого никогда не изменится.

В общем, если вы собираетесь использовать результат запроса более одного раза, всегда полезно сохранять его через ToList() или ToArray(). Это особенно верно, если ваш запрос LINQ является «дорогим», поскольку он предотвращает выполнение дорогостоящей операции более одного раза.

Как правило, если вы собираетесь только перечислять результаты, я бы оставил его как IEnumerable<T>. Если вы планируете хранить результаты или использовать их более одного раза, может оказаться полезным их хранение в коллекции.

Еще одно место, на которое следует обратить внимание, — это возврат результатов в общедоступный API. Хотя часто полезно возвращать IEnumerable<T>, в зависимости от ожидаемого варианта использования вы можете рассмотреть возможность использования ToList(), чтобы предотвратить повторное выполнение операции.

Что касается использования ToList() или ToArray(), это действительно зависит от того, как вы будете использовать результаты. Стоимость, связанная с каждым из них, почти одинакова (накладные расходы на выполнение ToList() на самом деле немного ниже, если ввод не ICollection<T>). Как правило, я предпочитаю ToList() ToArray(), если у меня нет особой потребности или желания в массиве.

person Reed Copsey    schedule 18.08.2012
comment
Отличный момент о публичной возвращаемой стоимости. Я уже сталкивался с этим делом. - person HappyNomad; 18.08.2012
comment
И та же предосторожность применяется, когда вы передаете результат другому методу. - person HappyNomad; 18.08.2012

ToList вызывает конструктор List<T>(IEnumerable<T>) для создания List<T>, а ToArrary использует внутренний класс Buffer<T> для увеличения массива.

Если исходная коллекция (IEnumerable<T>) реализует интерфейс ICollection, эти два метода используют одинаковую логику кода для копирования данных.

ICollection.CopyTo(array, 0);  

В противном случае ToList создаст List<T> динамически. Пока ToArray копирует элементы один за другим в новый массив. Если массив заполнен, метод удваивает размер массива для хранения данных. Наконец, метод возвращает другой массив на основе размера исходной коллекции.

person Hassan Boutougha    schedule 18.08.2012
comment
К сведению: ToList() и ToArray() используют почти идентичные механизмы построения (удвоение размера массива и т. д.). Основное отличие состоит в том, что ToArray потенциально более дорог, так как ему обычно требуется одна дополнительная копия массива в самом конце, так как он должен получить размер вывода, соответствующий емкости. - person Reed Copsey; 18.08.2012
comment
@HappyNomad FYI - заголовок вашего вопроса предполагает этот ответ, но текст вопроса нет (из-за этого я попытался включить оба варианта в свой ответ) - person Reed Copsey; 18.08.2012