匿名类型的 属性 的简单类型的实例怎么可能不总是 return 相同的值?

How can an instance of an anonymous type's property of a simple type not always return the same value?

我真的不知道如何提出这个问题,但当我看到这种行为时,我真的很困惑。真的应该是这样吗?

var data = new List<int> { 2, 4, 1 };//some dummy data for this example
var ii = 1;
var dataWithIds = data.Select(x => new
{
    id = ii++,
    value = x
});//I thought ii now would be 4, but it's still 1
var firstId = dataWithIds.First().id;//== 1, as expected. Now ii is 2
var alsoFirstId = dataWithIds.First().id;//== 2, not expected. Now ii is 3
ii = 1000;
var okMaybeItDoesNotWorkAsAnIdThen = dataWithIds.First().id;//==1000. Now ii is 1001

使用 new {id = ii++} 我认为 id 属性 总是 return ii 在 declaration/instantiation 处的值(然后 ii 在 declaration/instantiation 处递增),但看起来这实际上是 returns 调用 属性 时 ii 的值(然后当调用 属性 时 ii 递增)。

我的目的是创建一些数据的列表(或者实际上是一个匿名类型的 IEnumerable,当然比这个示例代码中的多一点),包括一个(自动递增的)ID。

我有办法解决这个问题(例如只添加 .ToList()),但如果知道它为什么这样工作会很有趣。还花了一些时间才发现这并没有像我预期的那样工作(以及它是如何工作的),所以希望这可以对其他人有所帮助。

你必须了解 LINQ 的工作原理:

        var data = new List<int> { 2, 4, 1 };//some dummy data for this example
        var ii = 1;
        var dataWithIds = data.Select(x => new
        {
            id = ii++,
            value = x
        }); // Nothing is executed at this point. Only an expression tree is created in memory.
        var firstId = dataWithIds.First().id; // The First() executes the ii++ 
        var alsoFirstId = dataWithIds.First().id; // The First() executes the ii++

我知道这可能令人费解,但它确实是一个美丽的构造。您可以延迟代码的执行,直到定义了所有需要的执行。这在查询数据库时特别有效。您可以推迟执行,直到所有过滤器、连接等都已声明。在执行 .ToList()、.First() 等时,请求作为一个有时很大的查询发送到数据库。

对于这种情况,您可以使用带有附加索引参数的 Select 重载。

var dataWithIds = data.Select((x, i) => new
{
    id = i + 1,
    value = x
});

根据你的问题。如果局部变量是在 LINQ 查询之外定义的,它将被编译器自动包装到带有 属性 ii 的隐藏 class 中,并且这个 class 的实例将在每个 [=13] 之后被重用=]、CountToList 等(枚举的函数)。这就是为什么您会看到“奇怪”的结果。叫做Closures还有很多解释What are 'closures' in .NET?