如何强制我的lambda表达式早期评估?修复lambda表达式的怪异

本文关键字:表达式 lambda 修复 何强制 我的 评估 | 更新日期: 2023-09-27 18:03:28

我写了以下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列表强制转换为一个数组来获得正确的结果,但这感觉像是一个拼凑。

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

如何强制我的lambda表达式早期评估?修复lambda表达式的怪异

您正在使用LINQ。您需要执行一个急切操作来使它执行. select。ToList()是一个很好的操作符。List是泛型的,可以直接赋值给IEnumberable。

在使用LINQ的情况下,它默认执行惰性求值。ToList/eager操作强制执行选择。在使用这些操作符之前,不会执行操作。这就像在ADO中执行SQL。NET之类的。如果您有语句"Select * from users",它实际上不会执行查询,直到您做了额外的事情。ToList使select执行。

关闭变量,而不是值。

创建变量的本地副本,以便从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);
}

然后读这个:

http://msdn.microsoft.com/en-us/vcsharp/hh264182

edit -强制动态求值也可以像其他人建议的那样工作,但是大多数情况下动态求值最终会慢得多。

Select(...)之后调用ToList()ToArray()

实际上这个问题是关于查找创建的,这可以通过标准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) };

在这种情况下,所有的位置将被一次下载,并分组在内存中。此外,在您尝试访问结果或将其转换为dictionary之前,不会执行此查询:

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