C# 将枚举与列表传递给视图会产生巨大的性能差异
简言之,我有一个观点:C# 将枚举与列表传递给视图会产生巨大的性能差异,c#,asp.net-mvc,C#,Asp.net Mvc,简言之,我有一个观点: @model IEnumerable<MyModel> 或 通过第一个(IEnumerable)比通过第二个(已转换为列表)慢 为什么? 我假设这与Model.Count()有关,并且可能它必须为每个周期将IEnumerable转换为列表,但即使只有100个条目,速度差也会从几乎立即变为8秒;DR:改用foreach。请参阅底部的代码 这基本上与ASP.NET无关——这是ASP.NET的工作方式和扩展方法,特别是对于延迟创建的序列 当你调用一个序列时,它会通
@model IEnumerable<MyModel>
或
通过第一个(IEnumerable)比通过第二个(已转换为列表)慢
为什么?
我假设这与Model.Count()有关,并且可能它必须为每个周期将
IEnumerable
转换为列表
,但即使只有100个条目,速度差也会从几乎立即变为8秒;DR:改用foreach
。请参阅底部的代码
这基本上与ASP.NET无关——这是ASP.NET的工作方式和扩展方法,特别是对于延迟创建的序列
当你调用一个序列时,它会通过询问原始序列的每个元素来创建一个新的序列,但是在这之后,你可以做任何事情(通过索引访问、获取计数等),而根本不需要咨询序列。它不仅不需要参考序列,而且LINQ还为实现和何时调用它们进行了优化
如果在未实现IList的IEnumerable
上调用Count()
(或其他几个有帮助的接口中的任何一个),它必须从开始到结束循环整个序列。如果延迟计算序列(例如,使用带有yield
语句的迭代器块),则意味着再次执行工作
ElementAt()
与之类似,只是它不必到达最末端-它只需要到达指定的元素
下面是一个完整的控制台应用程序,它非常清楚地演示了问题-请仔细运行它并确保您理解输出:
using System;
using System.Collections.Generic;
using System.Linq;
class Test
{
static void Main()
{
var sequence = CreateSequence();
ConsumeList(sequence);
ConsumeSequence(sequence);
}
static void ConsumeList(IEnumerable<int> sequence)
{
Console.WriteLine("Start of ConsumeList");
var list = sequence.ToList();
Console.WriteLine("ToList has completed - iterating");
for (int i = 0; i < list.Count(); i++)
{
var element = list.ElementAt(i);
Console.WriteLine($"Element {i} is {element}");
}
Console.WriteLine("End of ConsumeList");
Console.WriteLine();
}
static void ConsumeSequence(IEnumerable<int> sequence)
{
Console.WriteLine("Start of ConsumeSequence");
var list = sequence.ToList();
for (int i = 0; i < sequence.Count(); i++)
{
var element = sequence.ElementAt(i);
Console.WriteLine($"Element {i} is {element}");
}
Console.WriteLine("End of ConsumeSequence");
}
static IEnumerable<int> CreateSequence()
{
for (int i = 0; i < 5; i++)
{
var value = i * 2;
Console.WriteLine($"Yielding {value}");
yield return value;
}
}
}
现在棘手的部分是DisplayFor
是否会做正确的事情。可能不会-我对HtmlHelper
了解得不够,无法确切了解那里发生了什么。您可能需要对代码进行更多的更改,以使其正常工作
如果确实需要通过索引访问元素,我会将模型更改为列表
,并使用计数
属性和常规索引器:
@for(int i=0;im[i].LoggedInUser)
@DisplayFor(m=>m[i].DateCreated)
}
这样你就不会有隐藏的“也许它会很快,也许它不会”依赖关系。我看到的问题是使用IEnumerable-Count()和ElementAt(I)的两个方法。如果它不是没有随机访问优化的集合(例如SQL查询或类似流的枚举器)每次调用这些方法时,您都将执行查询。当您执行ToList时,您将执行enumerable直到最后,并将结果存储在内存中。您作为模型传递的具体内容是什么?作为序列公开的列表可能与延迟执行序列(如开放数据库查询或网络查询)的行为非常不同。IEnumerable的本质是它可以表达这两种情况。谢谢。我最初有一个
foreach
循环,但去了几家公司,试图让DropDownLists将它们的值传递回控制器,最后得到了这个int
循环。今天早上我发现我的初始模型设置不正确而且DropDownList的填充比最初想象的要容易得多,所以我将进行重构,希望最终回到foreach
var x = new DAL().GetList(); // returns IEnumerable<MyModel>
var x = new DAL().GetList().ToList();
using System;
using System.Collections.Generic;
using System.Linq;
class Test
{
static void Main()
{
var sequence = CreateSequence();
ConsumeList(sequence);
ConsumeSequence(sequence);
}
static void ConsumeList(IEnumerable<int> sequence)
{
Console.WriteLine("Start of ConsumeList");
var list = sequence.ToList();
Console.WriteLine("ToList has completed - iterating");
for (int i = 0; i < list.Count(); i++)
{
var element = list.ElementAt(i);
Console.WriteLine($"Element {i} is {element}");
}
Console.WriteLine("End of ConsumeList");
Console.WriteLine();
}
static void ConsumeSequence(IEnumerable<int> sequence)
{
Console.WriteLine("Start of ConsumeSequence");
var list = sequence.ToList();
for (int i = 0; i < sequence.Count(); i++)
{
var element = sequence.ElementAt(i);
Console.WriteLine($"Element {i} is {element}");
}
Console.WriteLine("End of ConsumeSequence");
}
static IEnumerable<int> CreateSequence()
{
for (int i = 0; i < 5; i++)
{
var value = i * 2;
Console.WriteLine($"Yielding {value}");
yield return value;
}
}
}