假设我们有一个源IEnumerable序列:
IEnumerable<Tuple<int, int>> source = new [] {
new Tuple<int, int>(1, 2),
new Tuple<int, int>(2, 3),
new Tuple<int, int>(3, 2),
new Tuple<int, int>(5, 2),
new Tuple<int, int>(2, 0),
};
我们想要应用一些过滤器和一些转换:
IEnumerable<int> result1 = source.Where(t => (t.Item1 + t.Item2) % 2 == 0)
.Select(t => t.Item2)
.Select(i => 1 / i);
IEnumerable<int> result2 = from t in source
where (t.Item1 + t.Item2) % 2 == 0
let i = t.Item2
select 1 / i;
这两个查询是等价的,两者都会在最后一项上抛出DivideByZeroException
。
但是,当枚举第二个查询时,VS调试器将让我检查整个查询,因此非常方便地确定问题的根源。
但是,枚举第一个查询时没有等效的帮助。检查LINQ实现不会产生有用的数据,可能是由于二进制文件已经优化:
有没有办法有效地检查&#34;堆栈中的可枚举值&#34;不使用查询语法的IEnumerable
s?查询语法不是一个选项,因为它不可能共享代码(即,转换是非常重要的并且不止一次使用)。
答案 0 :(得分:1)
但你可以调试第一个。只需在任何一个lambdas上插入一个断点,你就可以自由地检查参数值或其他任何范围。
调试时,您可以检查(在第一个Where
)t
t.Item1
,t
等内的情况下的值
至于您在第二个查询中执行最终select
而不是第一个查询时可以检查IEnumerable<int> result1 = source.Where(t => (t.Item1 + t.Item2) % 2 == 0)
.Select(t => new
{
t,
i = t.Item2,
})
.Select(result => 1 / result.i);
的原因,原因是您没有创建等效查询。您编写的第二个查询,当编译器写出时,将不生成类似您的第一个查询。它会巧妙地创造出一些东西,但仍然显着不同。它会创建这样的东西:
let
let
调用不只是选择该值,因为您编写的第一个查询会执行此操作。它选择一个新的匿名类型,从t
子句和前一个值中提取值,然后修改后续查询以提取适当的变量。这就是为什么“先前”变量(即select
仍然在查询结束时的范围内(在编译时和运行时;仅此一点应该是你的一个大提示)。使用上面提供的查询,当打破result.t
时,您可以通过调试器看到{{1}}的值。