LINQ的延期执行,但如何?

Tim*_*ter 24 .net c# linq lazy-evaluation

这一定非常简单.但无论如何我会问它,因为我认为其他人也会挣扎.为什么以下简单的LINQ查询不会始终使用新的变量值而不是始终使用第一个?

static void Main(string[] args)
{
    Console.WriteLine("Enter something:");
    string input = Console.ReadLine();       // for example ABC123
    var digits = input.Where(Char.IsDigit);  // 123
    while (digits.Any())
    {
        Console.WriteLine("Enter a string which doesn't contain digits");
        input = Console.ReadLine();         // for example ABC
    }
    Console.WriteLine("Bye");
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

在注释的示例中,它将进入循环,因为输入ABC123包含数字.但它永远不会离开它,即使你输入类似ABC,因为digits仍然是123.

那么为什么LINQ查询不会评估新input值但始终是第一个?

我知道我可以通过这个附加线修复它:

while (digits.Any())
{
    Console.WriteLine("Enter a string which doesn't contain digits");
    input = Console.ReadLine();          
    digits = input.Where(Char.IsDigit);  // now it works as expected
}
Run Code Online (Sandbox Code Playgroud)

或 - 更优雅 - 直接在循环中使用查询:

while (input.Any(Char.IsDigit))
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 41

不同之处在于您正在更改input变量的值,而不是变量引用的对象的内容 ...因此digits仍然引用原始集合.

与此代码比较:

List<char> input = new List<char>(Console.ReadLine());
var digits = input.Where(Char.IsDigit);  // 123
while (digits.Any())
{
    Console.WriteLine("Enter a string which doesn't contain digits");
    input.Clear();
    input.AddRange(Console.ReadLine());
}
Run Code Online (Sandbox Code Playgroud)

这一次,我们正在修改引用的集合的内容input - 并且digits实际上是对该集合的视图,我们可以看到更改.


Tho*_*que 10

您正在为其分配新值input,但digits序列仍然是从初始值派生的input.换句话说,当你这样做digits = input.Where(Char.IsDigit),它抓住了当前值的的input变量,而不是变量本身.分配新值对其input没有影响digits.


Bry*_*tts 6

这一行:

input.Where(Char.IsDigit)
Run Code Online (Sandbox Code Playgroud)

相当于:

Enumerable.Where(input, Char.IsDigit)
Run Code Online (Sandbox Code Playgroud)

因此,该值input被作为的源传递.Where查询,而不是一个参考input.

您提出的第一个修复方法有效,因为它使用input先前行上新分配的值.