C# 如何强制lambda表达式尽早求值?修正lambda表情的古怪?

C# 如何强制lambda表达式尽早求值?修正lambda表情的古怪?,c#,.net,linq,lambda,C#,.net,Linq,Lambda,我编写了以下C代码: _locationsByRegion = new Dictionary<string, IEnumerable<string>>(); foreach (string regionId in regionIds) { IEnumerable<string> locationIds = Locations .Where(location => location.regionId.ToUpper() == reg

我编写了以下C代码:

_locationsByRegion = new Dictionary<string, IEnumerable<string>>();
foreach (string regionId in regionIds)
{
    IEnumerable<string> locationIds = Locations
        .Where(location => location.regionId.ToUpper() == regionId.ToUpper())
        .Select(location => location.LocationId); //If I cast to an array here, it works.
    _locationsByRegion.Add(regionId, LocationIdsIds);
}
此代码旨在创建一个字典,其中我的区域ID作为键,位置ID列表作为值

然而,实际发生的是,我得到了一个以区域id为键的字典,但每个键的值都是相同的:它是RegionID中最后一个区域id的位置列表

看起来这是lambda表达式求值方式的产物。我可以通过将位置ID列表强制转换为一个数组来获得正确的结果,但这感觉像是一个难题


处理这种情况的最佳做法是什么?

在选择后呼叫ToList或ToArray。。。。因此,整个集合将在此处进行计算。

您对变量的结束,而不是对值的结束

创建变量的本地副本,以便从foreach循环捕获当前值:

_locationsByRegion = new Dictionary<string, IEnumerable<string>>();
foreach (string regionId in regionIds)
{
    var regionToUpper = regionId.ToUpper();
    IEnumerable<string> locationIds = Locations
        .Where(location => location.regionId.ToUpper() == regionToUpper)
        .Select(location => location.LocationId); //If I cast to an array here, it works.
    _locationsByRegion.Add(regionId, LocationIdsIds);
}
然后读一读:


编辑-强制进行急切的评估也会像其他人建议的那样起作用,但大多数情况下,急切的评估最终会慢得多。

您使用的是LINQ。您需要执行渴望操作,使其执行。选择。托利斯特是一个很好的接线员。列表是通用的,可以直接分配给IEnumberable


在使用LINQ的情况下,默认情况下它会执行惰性计算。ToList/eager操作强制执行选择。在使用这些运算符之一之前,不会执行该操作。这就像在ADO.NET中执行SQL一样。如果您有Select*语句,则在您执行额外操作之前,该语句实际上不会执行查询。ToList使select执行。

实际上,问题是关于查找创建,使用标准LINQ组联接可以更简单地实现:

var query = from regionId in regionIds
            join location in Locations
            on regionId.ToLower() equals location.regionId.ToLower() into g
            select new { RegionID = regionId, 
                         Locations = g.Select(location => location.LocationId) };
在这种情况下,将立即下载所有位置,并在内存中分组。此外,在您尝试访问结果或将其转换为字典之前,不会执行此查询:

var locationsByRegion = query.ToDictionary(x => x.RegionID, x => x.Locations);

正如我在问题中提到的,我尝试调用ToArray,然后立即将其转换回一个可枚举项,但这感觉像是一个乱七八糟的问题。还有更好的方法吗?@米粉饼干,就像他说的,使用ToList它是一个IEnumerable,因此没有重铸。好吧,ToArray也是IEnumerable。这是不可数的。但是这个列表是数不清的。所以ToList就可以了。是的,忘了给它添加泛型。感谢您澄清@Ivan DanilovNo,这是闭包如何捕获变量的副产品,有时是非常有用的副产品。另一方面:您可能需要查看或删除循环。@Rice这意味着任何将枚举集合、ToArray、ToList的操作,在使用LINQ的情况下,默认情况下它会执行惰性计算。ToList/贪婪操作强制执行选择。在使用这些运算符之一之前,不会执行该操作。这就像在ADO.NET中执行SQL一样。如果您有Select*语句,则在您执行额外操作之前,该语句实际上不会执行查询。ToList使select执行。贪婪是误导;人们通常认为贪婪操作是在其操作中使用贪婪算法的操作;也就是说,一种试图通过计算局部而非全局最优行为来优化其行为的算法。如果你想简明扼要地描述一个懒惰或延迟计算的反面,那就选择渴望而不是贪婪。虽然.ToList会起作用,但我认为它不是这个问题的最佳解决方案。大多数情况下,您希望对序列进行惰性评估。。。对于性能来说,这是一种更好的方法,要使其工作,您所要做的就是将regionId变量分配给循环中的一个变量。@Rice Fare Cookies:有关糟糕的详细信息,请参阅。这样您仍然可以进行惰性评估。我不能确定,但在大多数情况下,这只是额外的复杂性。此外,如果计算有一些副作用,您可以在调试器中查看值时轻松触发它们。如果你不需要惰性评估,就不要使用它。嗯,问题还在于让lambda尽早接受评估。这不是你在这里建议的。@Ivan海报想要尽早评估以修复“古怪”,我指出了他为什么会看到这一点,以便他能够准确地理解他所看到的行为。我没有说这是-1。只是澄清了OP很可能需要急切的评估,而不是正确的结束。即使正确的关闭有效,嗯。。。正确:这个+1.如果你没有必要,就不要过早评估。在这种情况下,我显然需要尽早评估,否则我一点也不理解。