Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/265.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是不是;foreach“;导致Linq重复执行?_C#_.net_Linq - Fatal编程技术网

C# 是不是;foreach“;导致Linq重复执行?

C# 是不是;foreach“;导致Linq重复执行?,c#,.net,linq,C#,.net,Linq,我第一次在.NET中使用实体框架,并且一直在编写LINQ查询,以便从我的模型中获取信息。我想从一开始就养成良好的编程习惯,所以我一直在研究编写这些查询的最佳方法,并得到它们的结果。不幸的是,在浏览Stack Exchange时,我似乎遇到了两种相互矛盾的解释,即延迟/立即执行如何与LINQ协同工作: foreach导致在循环的每个迭代中执行查询: 有疑问的是,这意味着需要调用“ToList()”以立即计算查询,因为foreach正在重复计算数据源上的查询,这大大降低了操作的速度 另一个例子是

我第一次在.NET中使用实体框架,并且一直在编写LINQ查询,以便从我的模型中获取信息。我想从一开始就养成良好的编程习惯,所以我一直在研究编写这些查询的最佳方法,并得到它们的结果。不幸的是,在浏览Stack Exchange时,我似乎遇到了两种相互矛盾的解释,即延迟/立即执行如何与LINQ协同工作:

  • foreach导致在循环的每个迭代中执行查询:
有疑问的是,这意味着需要调用“ToList()”以立即计算查询,因为foreach正在重复计算数据源上的查询,这大大降低了操作的速度

另一个例子是问题,其中接受的答案还意味着对查询调用“ToList()”将提高性能

  • foreach使查询执行一次,并且可以安全地与LINQ一起使用
有疑问的是,这意味着foreach会导致建立一个枚举,并且不会每次查询数据源

继续浏览该网站会发现许多问题,其中“foreach循环中的重复执行”是性能问题的罪魁祸首,还有许多其他答案表明foreach将适当地从数据源抓取单个查询,这意味着这两种解释似乎都是有效的。如果“ToList()”假设不正确(美国东部时间2013-06-05下午1:51的大多数当前答案似乎都暗示了这一点),那么这种误解从何而来?这些解释中是否有一种是准确的,另一种是不准确的,或者是否有不同的情况会导致LINQ查询的计算结果不同


Edit:除了下面公认的答案之外,我还向程序员提出了以下问题,这对我理解查询执行非常有帮助,特别是在循环过程中可能导致多个数据源命中的陷阱,我认为这将有助于其他对这个问题感兴趣的人:

使用LINQ,即使没有实体,您将得到的是延迟执行是有效的。 只有通过强制迭代,才能计算实际的linq表达式。 从这个意义上讲,每次使用linq表达式时,都将对其进行求值

现在对于实体,这仍然是一样的,但是这里有更多的功能在工作。 当实体框架第一次看到这个表达式时,它会查看他是否已经执行了这个查询。如果没有,它将转到数据库并获取数据,设置其内部内存模型并将数据返回给您。如果实体框架看到它已经预先获取了数据,它就不会去数据库并使用它之前设置的内存模型将数据返回给您

这可以让你的生活更轻松,但也可能是一种痛苦。例如,如果使用linq表达式从表中请求所有记录。实体框架将加载表中的所有数据。如果以后对同一个linq表达式求值,即使当时删除或添加了记录,也会得到相同的结果

实体框架是一件复杂的事情。当然,有一些方法可以让它重新执行查询,考虑到它在自己的内存模型等方面所做的更改


我建议阅读Julia Lerman的“编程实体框架”。它解决了许多问题,比如您现在遇到的问题。

通常,LINQ使用延迟执行。如果使用像
First()
FirstOrDefault()
这样的方法,查询将立即执行。当你做这样的事情时

foreach(string s in MyObjects.Select(x => x.AStringProp))
List<string> names = People.Select(x => x.Name).ToList();
foreach (string name in names)
结果以流式方式检索,即一个接一个地检索。每次迭代器调用
MoveNext
时,投影将应用于下一个对象。如果要在其中设置一个
,它将首先应用过滤器,然后应用投影

如果你做了这样的事情

foreach(string s in MyObjects.Select(x => x.AStringProp))
List<string> names = People.Select(x => x.Name).ToList();
foreach (string name in names)
List Name=People.Select(x=>x.Name.ToList();
foreach(名称中的字符串名称)
那么我认为这是一个浪费的行动
ToList()
将强制执行查询,枚举
People
列表并应用
x=>x.Name
投影。之后,您将再次枚举该列表。因此,除非您有充分的理由将数据放在列表(而不是IEnumerale)中,否则您只是在浪费CPU周期

一般来说,在使用foreach枚举的集合上使用LINQ查询不会比任何其他类似且实用的选项的性能差

另外值得注意的是,我们鼓励实现LINQ提供程序的人员像在Microsoft提供的提供程序中一样使用通用方法,但并不要求他们这样做。如果我去写一个LINQ到HTML或LINQ到我的专有数据格式提供程序,就不能保证它以这种方式运行。也许数据的性质会使立即执行成为唯一可行的选择


还有,最终编辑;如果你对乔恩·斯基特的《C#Depth》感兴趣,这本书内容丰富,读起来很棒。我的回答总结了这本书的几页(希望有合理的准确性),但如果你想了解LINQ如何在封面下工作的更多细节,这是一个很好的地方。foreach本身只运行一次数据。事实上,它只运行过一次。您不能向前或向后看,也不能用
for
循环的方式更改索引

但是,如果代码中有多个
foreach
s,它们都在同一个LINQ查询上运行,则可能会多次执行查询。但这完全取决于数据。如果在LINQ上迭代
First foreach starting
Doing where on 1
Doing where on 2
Foreached where on 2
Doing where on 3
Doing where on 4
Foreached where on 4
Doing where on 5
Doing where on 6
Foreached where on 6
Doing where on 7
Doing where on 8
Foreached where on 8
Doing where on 9
Doing where on 10
Foreached where on 10
First foreach ending
Second foreach starting
Doing where on 1
Doing where on 2
Foreached where on 2 for the second time.
Doing where on 3
Doing where on 4
Foreached where on 4 for the second time.
Doing where on 5
Doing where on 6
Foreached where on 6 for the second time.
Doing where on 7
Doing where on 8
Foreached where on 8 for the second time.
Doing where on 9
Doing where on 10
Foreached where on 10 for the second time.
Second foreach ending
// Main method:
static void Main(string[] args)
{
    IEnumerable<int> ints = Enumerable.Range(0, 100);

    var query = ints.Where(x =>
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.Write($"{x}**, ");
        return x % 2 == 0;
    });

    DoForeach(query, "query");
    DoForeach(query, "query.ToList()");

    Console.ForegroundColor = ConsoleColor.White;
}

// DoForeach method:
private static void DoForeach(IEnumerable<int> collection, string collectionName)
{
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine("\n--- {0} FOREACH BEGIN: ---", collectionName);

    if (collectionName.Contains("query.ToList()"))
        collection = collection.ToList();

    foreach (var item in collection)
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.Write($"{item}, ");
    }

    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine("\n--- {0} FOREACH END ---", collectionName);
}