AsList()比使用返回IEnumerable的IDbConnection.Query()的ToList()更好吗?

时间:2017-12-13 13:38:06

标签: c# performance linq dapper database-performance

我从Marc Gravell(@MarcGravell)读到这个答案:https://stackoverflow.com/a/47790712/5779732

最后一行说:

  

作为对代码的次要优化:首选AsList()到ToList()以避免创建副本。

该声明约为QueryMultiple(),返回GridReader

据我了解,System.Linq提供了一种扩展方法IEnumerable.ToList()。以下内容来自Microsoft关于ToList()

  

ToList(IEnumerable)方法强制立即进行查询评估,并返回包含查询结果的List。您可以将此方法附加到查询中,以获取查询结果的缓存副本。

IDbConnection.Query()将始终返回IEnumerablenull。在调用代码时可以轻松完成空检查。那么AsList会有什么不同呢?

如果我的理解是正确的,AsList将始终在内部调用ToList来创建副本。

考虑到这一点,AsList()优于ToList()IDbConnection.Query()返回IEnumerable?如是;为什么呢?

在这种情况下,AsList()内部是什么使它成为更好的选择?

2 个答案:

答案 0 :(得分:10)

AsList是一种自定义Dapper扩展方法。它所做的就是检查传递给它的IEnumerable<T>是否真的List<T>。如果是 - 它返回它,只是转换为List<T>。如果不是 - 它会定期调用ToList。关键是 - ToList()总是创建一个副本,即使你传递给它的是一个列表。 AsList()方法避免执行此副本,因此如果不需要此类副本,则非常有用。

在此特定方案中,您有以下代码:

multipleresult.Read<MerchantProduct>()

其中multipleresultGridReaderRead具有buffered参数,默认情况下为true。如果真实 - Read将真正返回List<T>,那么通过调用ToList,您将无需多少理由再次复制该列表。

IDbConnection.Query()同样如此 - 也有buffered参数,默认情况下为true,因此默认情况下也会返回List<T>

如果您更喜欢使用ToList(),则可以将buffered: false传递给Query()Read(),以避免创建其他副本。

答案 1 :(得分:2)

此扩展程序是一个自定义dapper扩展程序,在调用ToList之前会进行额外检查。 Source

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

直到现在这并没有真正受到伤害。现在新的List被初始化并填充,因为源不是列表而且不能被转换。所以效率低下。但如果逻辑也依赖于列表是相同的引用,这将不再起作用。

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

使用数组后始终为false。所以代码被默默地打破了。

简而言之:我不喜欢AsList方法。您可以随时自行检查类型。