您是否有在您与LINQ查询结果交易时使用的默认类型?
默认情况下,LINQ将返回一个IEnumerable<>
或者一个IOrderedEnumerable<>
.我们发现a List<>
对我们来说通常更有用,因此ToList()
大多数时候习惯于查询我们的查询,并且肯定List<>
在我们的函数参数和返回值中使用.
唯一的例外是LINQ to SQL,其中调用.ToList()
将IEnumerable
过早地枚举.
我们也广泛使用WCF,其默认集合类型是System.Array
.我们总是System.Collections.Generic.List
在VS2008的"服务引用设置"对话框中将其更改为与我们的其余代码库保持一致.
你是做什么?
ToList
总是立即评估序列 - 而不仅仅是在LINQ to SQL中.如果你想要,那很好 - 但并不总是合适的.
就个人而言,我会尽量避免声明您List
直接返回- 通常IList
更合适,并允许您稍后更改为其他实现.当然,有些操作只是List
自己指定的......这种决定总是很棘手.
编辑:(我会把它放在评论中,但它会太笨重.)延迟执行允许您处理太大而不适合内存的数据源.例如,如果您正在处理日志文件 - 将它们从一种格式转换为另一种格式,将它们上传到数据库中,计算出一些统计数据或类似的东西 - 您可以通过流式处理它来处理任意数量的数据,但你真的不要想吸一切到内存中.这可能不是您特定应用的关注点,但需要牢记这一点.
我们有相同的场景 - 与服务器的WCF通信,服务器使用LINQtoSQL.
我们在从服务器请求对象时使用.ToArray(),因为客户端更改列表是"非法的".(意思是,没有任何意图支持".Add",".删除"等).
但是,虽然仍在服务器上,但我建议您将其保留为默认设置(不是IEnumerable,而是IQueryable).这样,如果您希望根据某些条件进行更多过滤,则在SQL端进行过滤仍然是STILL,直到进行评估.
这是非常重要的一点,因为它意味着令人难以置信的性能增益或损失取决于您的工作.
例:
// This is just an example... imagine this is on the server only. It's the // basic method that gets the list of clients. private IEnumerableGetClients() { var result = MyDataContext.Clients; return result.AsEnumerable(); } // This method here is actually called by the user... public Client[] GetClientsForLoggedInUser() { var clients = GetClients().Where(client=> client.Owner == currentUser); return clients.ToArray(); }
你看到那里发生了什么吗?"GetClients"方法将强制从数据库下载所有"客户端"......然后,在GetClientsForLoogedInUser方法中将发生Where子句以对其进行过滤.
现在,注意一点变化:
private IQueryableGetClients() { var result = MyDataContext.Clients; return result.AsQueryable(); }
现在,直到调用".ToArray"才会进行实际评估......并且SQL将进行过滤.好多了!
在Linq-to-Objects的情况下,List
从函数返回并不像返回那样好IList
,正如VENERABLE SKEET指出的那样.但通常你仍然可以做得更好.如果你要返回的东西应该是不可变的,那么IList是一个糟糕的选择,因为它邀请调用者添加或删除东西.
例如,有时你有一个方法或属性返回Linq查询的结果或用于yield return
懒惰地生成一个列表,然后你意识到最好在你第一次调用时这样做,将结果缓存到a List
然后返回缓存版本.返回时IList
可能是一个坏主意,因为调用者可能会出于自己的目的修改列表,这会破坏您的缓存,使其更改对所有其他调用者可见.
最好返回IEnumerable
,所以他们只有前向迭代.如果调用者想要快速随机访问,即他们希望他们可以使用[]来通过索引访问,他们可以使用ElementAt
,Linq定义它以便它悄悄地嗅探IList
并使用它(如果可用的话),如果没有,它会进行哑线性查找.
我曾经使用ToList
过的一件事是当我有一个复杂的Linq表达式系统与yield return
用于过滤或转换列表的自定义运算符混合在一起时.在调试器中单步执行可能会因为它在执行延迟评估时跳转而变得非常混乱,因此我有时会临时将ToList()添加到几个位置,以便我可以更轻松地遵循执行路径.(虽然如果你正在执行的事情有副作用,这可能会改变程序的含义.)