Является AsList() лучше, чем ToList () с IDbConnection.Query () который возвращает IEnumerable?

я прочитал этот ответ от Марка Гравелла (@MarcGravell):https://stackoverflow.com/a/47790712/5779732

последняя строка говорит:

в качестве незначительной оптимизации вашего кода: предпочитайте AsList() to ToList (), чтобы избежать создания копии.

это утверждение о QueryMultiple() возвращает GridReader.

в моем понимании, System.Linq предоставляет метод расширения IEnumerable.ToList(). Ниже Microsoft о ToList().

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

IDbConnection.Query() всегда будет возвращаться IEnumerable или null. Null-check можно легко сделать в вызывающем коде. Какая разница AsList делает тогда?

если мое понимание правильно, AsList всегда будет внутренне вызов ToList который создаст копию.

учитывая это, это AsList() лучше, чем ToList() С IDbConnection.Query() возвращает IEnumerable? Если да, то почему?

что это AsList() делает ли внутренне это лучший выбор в этом случае?

2 ответов


AsList - это пользовательский метод расширения Dapper. Все, что он делает, это проверяет, если IEnumerable<T> вы переходите к нему действительно List<T>. Если это - он возвращает его обратно, просто бросает в List<T>. Если это не так - он вызывает regular ToList. Смысл есть - ToList() всегда создает копию, даже если то, что вы передаете ей уже список. AsList() метод избегает делать эту копию, и поэтому полезен, если такая копия не нужна.

в этом конкретном сценарии у вас есть следующее код:

multipleresult.Read<MerchantProduct>()

здесь multipleresult и GridReader. Read и buffered аргумент, который является true по умолчанию. Когда это правда -Read действительно вернется List<T>, путем вызова ToList вы скопируете этот список снова без особых причин.

то же самое верно для IDbConnection.Query() - тоже есть buffered параметр, который является true по умолчанию, поэтому он также по умолчанию возвращает List<T>.

если вы предпочитаете использовать ToList() - вы можете передать buffered: false to Query() или Read() чтобы не создавать дополнительные копии.


это расширение является пользовательским расширением dapper, которое делает дополнительную проверку перед вызовом ToList. источник:

public static List<T> AsList<T>(this IEnumerable<T> source) 
    => (source == null || source is List<T>) ? (List<T>)source : source.ToList();
  • ToList всегда создает новый List<T> экземпляр и заполняет его с данными элементами
  • AsList проверяет, является ли последовательность уже List<T>, тогда он просто бросит его

конечно, этот подход может быть более эффективным, потому что литье гораздо меньше работы, чем создание и наполнение чем-то новым. Так что это совсем другое.

это мнение основано, но я нахожу это опасно. Кто-то может упустить AsList читает ToList или просто не знают разницы. Это опасно, если кто-то изменит код позже.

так, например, метод, который принимает IEnumerable<T> использует AsList:

public static List<T> GetResult<T>(IEnumerable<T> seq)
{
    if(some condition here)
    {
        seq = seq.Where(some predicate here);
    }
    return seq.AsList()
}

теперь код вызвал этот метод со списком:

IEnumerable<string> sequence = (gets a list from somewhere)
List<string> userList = GetResult(sequence);

позже кто-то решает что массив более уместен здесь:

IEnumerable<string> sequence = (gets an array from somewhere)
List<string> userList = GetResult(sequence);

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

if(userList == seq)
{
    // do something
}

это всегда false как только массив будет использован . Код был взломан беззвучно.

короче говоря: мне не нравится AsList метод. Вы всегда можете проверить тип самостоятельно.