AsList() 比 ToList() 和 IDbConnection.Query() returns IEnumerable 更好吗?

Is AsList() better than ToList() with IDbConnection.Query() which returns IEnumerable?

我读了 Marc Gravell (@MarcGravell) 的回答:

最后一行说:

As a minor optimization to your code: prefer AsList() to ToList() to avoid creating a copy.

该语句是关于 QueryMultiple() 的,其中 return 是 GridReader

在我的理解中,System.Linq提供了一个扩展方法IEnumerable.ToList()。以下来自 Microsoft 关于 ToList().

The ToList(IEnumerable) method forces immediate query evaluation and returns a List that contains the query results. You can append this method to your query in order to obtain a cached copy of the query results.

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

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

考虑到这一点,AsList()ToList()IDbConnection.Query() 哪个 return 比 IEnumerable 好?如果是;为什么?

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

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

在此特定场景中,您有以下代码:

multipleresult.Read<MerchantProduct>()

其中 multipleresultGridReaderReadbuffered 参数,默认为真。当它为真时 - Read 将真正 return List<T>,因此通过调用 ToList 您将毫无理由地再次复制该列表。

对于IDbConnection.Query()也是如此-也有buffered参数,默认情况下为true,因此默认情况下也会return List<T>.

如果您更喜欢使用 ToList() - 您可以将 buffered: false 传递给 Query()Read() 以避免创建额外的副本。

这个扩展是一个自定义的 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);

到现在为止这并没有真正伤害到。现在初始化并填充了一个新的列表,因为源不是列表并且无法转换。所以它的效率较低。但如果逻辑也依赖于列表是相同的引用,这将不再有效。

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

一旦数组被使用,这总是 false。于是悄无声息地破解了密码。

长话短说:我不喜欢 AsList 方法。您可以随时自己检查类型。