C# 匿名类型的实例';简单类型的属性不总是返回相同的值?

C# 匿名类型的实例';简单类型的属性不总是返回相同的值?,c#,linq,anonymous-types,C#,Linq,Anonymous Types,我真的不知道如何界定这个问题,但当我看到这种行为时,我真的很困惑。真的应该是这样吗 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 st

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

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
var data=新列表{2,4,1}//本例中的一些虚拟数据
var ii=1;
var dataWithIds=data.Select(x=>new
{
id=ii++,
值=x
});//我以为我现在是4,但还是1
var firstId=dataWithIds.First().id;//==一如所料。现在我是2
var alsoFirstId=dataWithIds.First().id;//=2、不在预料之中。现在我3岁了
ii=1000;
var Okmaybeitdesnotworkasanidthen=dataWithIds.First().id//==1000现在我是1001
使用
new{id=ii++}
时,我认为id属性总是返回ii在声明/实例化时的值(然后ii在声明/实例化时递增),但这似乎实际上是在调用属性时返回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++
var data=新列表{2,4,1}//本例中的一些虚拟数据
var ii=1;
var dataWithIds=data.Select(x=>new
{
id=ii++,
值=x
}); // 此时不执行任何操作。在内存中仅创建表达式树。
var firstId=dataWithIds.First().id;//第一个()执行ii++
var alsoFirstId=dataWithIds.First().id;//第一个()执行++

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

对于这种情况,您可以使用
选择
重载和附加索引参数

var-dataWithIds=data.选择((x,i)=>new
{
id=i+1,
值=x
});

根据你的问题。如果局部变量是在LINQ查询外部定义的,编译器将自动将其包装到具有属性
ii
的隐藏类中,并且在每次
第一次
计数
ToList
等(枚举的函数)之后都将重用该类的实例。这就是为什么你会看到“奇怪”的结果。它被称为
闭包
,有很多解释

在Select和run程序中设置断点。当您执行第一个()时,您将看到ii++被执行。谢谢,@Paulma
var firstElement=dataWithIds.First();var x=firstElement.id;var y=firstElement.id//==十,。明白了检查
select
doc的备注。延期执行:谢谢,这也很有用。