LINQ是否缓存计算值?

Shr*_*roy 7 .net c# linq clr4.0

假设我有以下代码:

var X = XElement.Parse (@"
    <ROOT>
        <MUL v='2' />
        <MUL v='3' />
    </ROOT>
");
Enumerable.Range (1, 100)
    .Select (s => X.Elements ()
        .Select (t => Int32.Parse (t.Attribute ("v").Value))
        .Aggregate (s, (t, u) => t * u)
    )
    .ToList ()
    .ForEach (s => Console.WriteLine (s));
Run Code Online (Sandbox Code Playgroud)

什么是.NET运行时实际在这里做什么?难道解析和转换属性整数各100次,还是足够聪明弄清楚,它应该缓存解析值,而不是重复计算的范围中的每个元素?

而且,我怎么会自己搞清楚这样的事情呢?

在此先感谢您的帮助.

Mic*_*eld 2

自从我研究这段代码以来已经有一段时间了,但是,IIRC,Select有效的方法是简单地缓存Func您提供的代码并一次在源集合上运行它。Select/Aggregate因此,对于外部范围中的每个元素,它将像第一次一样运行内部序列。没有任何内置缓存正在进行——您必须自己在表达式中实现它。

如果您想自己解决这个问题,您有三个基本选择:

  1. 编译代码并使用ildasm查看IL;它是最准确的,但是,特别是对于 lambda 和闭包,您从 IL 获得的内容可能与您放入 C# 编译器中的内容完全不同。
  2. 使用dotPeek之类的工具将System.Linq.dll反编译为C#;再次强调,您从这些工具中得到的内容可能只是大致类似于原始源代码,但至少它是 C# 的(尤其是 dotPeek 做得非常好,而且是免费的。)
  3. 我个人的偏好 - 下载 .NET 4.0参考源并自行查找;这就是它的用途:) 你必须相信 MS 的参考源与用于生成二进制文件的实际源相匹配,但我没有看到任何充分的理由怀疑它们。
  4. 正如 @AllonGuralnek 所指出的,您可以在一行内的特定 lambda 表达式上设置断点;将光标放在 lambda 体内的某个位置,然后按 F9,它将仅在 lambda 上设置断点。(如果你做错了,它会以断点颜色突出显示整行;如果你做对了,它只会突出显示 lambda。)

  • 4. 将光标放在“=&gt;”后面,然后按 F9。这将在 lambda 内部放置一个断点,并在到达断点时中断。对每个 lambda 重复此操作,您就可以很好地跟踪何时调用的内容。 (2认同)