无法弄清楚为什么这个IEnumerable给出了这个答案

时间:2014-11-07 04:14:44

标签: c# ienumerable

以下代码的答案为 5

有人可以解释为什么会这样吗?如果您要将int d1 = x.Current替换为d1 = x.Current并在while循环上方声明d1,答案将是 2 ,我理解为什么会这样,但我不会# 39;不知道为什么它会这样做。

IEnumerable<int> num = new []{10,11,12,13,14,15,16};
IEnumerable<int> div = new [] {2,3,5};

var lazy = Enumerable.Empty<int>();
var x = div.GetEnumerator();

while(x.MoveNext()){
    int d1 = x.Current;
    lazy = lazy.Concat(num.Where(s=>s % d1 == 0));
}

int count = lazy.Distinct().Count();
Console.WriteLine("{0}",count);

编辑:这是给你答案2的片段。

IEnumerable<int> num = new []{10,11,12,13,14,15,16};
IEnumerable<int> div = new [] {2,3,5};

var lazy = Enumerable.Empty<int>();
var x = div.GetEnumerator();
int d1;

while(x.MoveNext()){
    d1 = x.Current;
    lazy = lazy.Concat(num.Where(s=>s % d1 == 0));
}

int count = lazy.Distinct().Count();
Console.WriteLine("{0}",count);

2 个答案:

答案 0 :(得分:4)

这是因为在lazy.Concat(num.Where(s=>s % d1 == 0))中延迟执行lambda表达式。

当您在循环内声明变量时,匿名方法的每个实例都会获得自己的变量。但是当你在循环外部声明变量时,它们都共享相同的变量,当然,当最终在这里执行lambda表达式时,变量的值只有一个值(循环中指定的最终值):

int count = lazy.Distinct().Count();

故事的寓意:谨防使用捕获的变量。它们通常很难推理,所以应该小心使用它们。

答案 1 :(得分:4)

(彼得打败了我,但是当他的回答出现时,我已经完成了一半,所以无论如何我都会发布它。)

您可以通过检测代码来更深入地了解差异。 lambda表达式中的检测是提供关键洞察力的东西:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Baseline.");
        Test1();
        Console.WriteLine("Modified.");
        Test2();
    }

    static void Test1()
    {
        IEnumerable<int> num = new[] { 10, 11, 12, 13, 14, 15, 16 };
        IEnumerable<int> div = new[] { 2, 3, 5 };

        var lazy = Enumerable.Empty<int>();
        var x = div.GetEnumerator();

        while (x.MoveNext())
        {
            int d1 = x.Current;
            Console.WriteLine("d1 = " + d1);
            lazy = lazy.Concat(num.Where(s => {bool result = s % d1 == 0; Console.WriteLine("s = " + s + ", d1 = " + d1); return result;}));
            Console.WriteLine("lazy has " + lazy.Count());
        }

        Console.WriteLine("Evaluating lazy.Distinct().Count()");        
        int count = lazy.Distinct().Count();
        Console.WriteLine("{0}", count);
    }

    static void Test2()
    {
        IEnumerable<int> num = new[] { 10, 11, 12, 13, 14, 15, 16 };
        IEnumerable<int> div = new[] { 2, 3, 5 };

        var lazy = Enumerable.Empty<int>();
        var x = div.GetEnumerator();
        int d1;

        while (x.MoveNext())
        {
            d1 = x.Current;
            Console.WriteLine("d1 = " + d1); 
            lazy = lazy.Concat(num.Where(s => {bool result = s % d1 == 0; Console.WriteLine("s = " + s + ", d1 = " + d1); return result;}));
            Console.WriteLine("lazy has " + lazy.Count());
        }

        Console.WriteLine("Evaluating lazy.Distinct().Count()");
        int count = lazy.Distinct().Count();
        Console.WriteLine("{0}", count);
    }
}

&#34;评估lazy.Distinct()。Count()&#34;如果打印出来,你会注意到两件可能让你感到惊讶的事情。

首先,该评估需要重新运行在循环中声明的lambda表达式。想到&#34;懒惰&#34;这很容易,但却是错误的。作为整数的集合。实际上它是一个用于创建整数集合的函数,因此计算不同的元素需要重新运行该函数。

其次,您会注意到两个评估之间的d1值不同。在第一种情况下,d1是2,而在第二种情况下,d1是5.原因是,正如Peter Duniho指出的那样,在循环外声明d1允许它保留循环结束时的值(所以你有5,div序​​列中的最后一个值),在循环中声明它需要重新计算它(所以你有2,div序​​列中的第一个元素)。