这一定非常简单。但无论如何我会问它,因为我认为其他人也会挣扎。为什么以下简单的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();
}
在注释示例中,它将进入循环,因为输入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
}
或 - 更优雅 - 直接在循环中使用查询:
while (input.Any(Char.IsDigit))
{
// ...
}
答案 0 :(得分:40)
不同之处在于您正在更改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());
}
这一次,我们正在修改input
引用的集合的内容 - 并且digits
实际上是对该集合的视图,我们可以看到改变。
答案 1 :(得分:10)
您要为input
分配新值,但digits
序列仍然来自input
的初始值。换句话说,当您执行digits = input.Where(Char.IsDigit)
时,会捕获input
变量的当前值,而不是变量本身。为input
分配新值对digits
无效。
答案 2 :(得分:6)
这一行:
input.Where(Char.IsDigit)
相当于:
Enumerable.Where(input, Char.IsDigit)
因此,input
的值作为.Where
查询的来源传递,而不是参考传递给input
。
您提出的第一个解决方案有效,因为它在先前的行上使用了新分配的input
值。
答案 3 :(得分:4)
可枚举数字是指创建可枚举项时input
包含的字符串的副本。它没有对input
变量的引用,并且更改input
中存储的值不会导致枚举的实现使用新值。
请记住,Where
是一种静态扩展方法,并接受您将其作为参数调用的对象。
答案 4 :(得分:4)
这几乎是一个评论,但包含结构化代码,因此我将其作为答案提交。
以下对代码的轻微修改将起作用:
Console.WriteLine("Enter something:");
string input = Console.ReadLine(); // for example ABC123
Func<bool> anyDigits = () => input.Any(Char.IsDigit); // will capture 'input' as a field
while (anyDigits())
{
Console.WriteLine("Enter a string which doesn't contain digits");
input = Console.ReadLine(); // for example ABC
}
Console.WriteLine("Bye");
Console.ReadLine();
此处input
由Func<bool>
类型的委托捕获(封闭)。
答案 5 :(得分:2)
我只是回答其他好答案的精确度,关于延迟执行。
即使尚未评估LINQ查询(使用.Any()
),查询内部也始终引用变量的初始内容。即使在 之后评估了某个新变量影响了LINQ查询,初始内容也不会发生变化,延迟执行将使用查询一直引用的初始内容:
var input = "ABC123";
var digits = input.Where(Char.IsDigit);
input = "NO DIGIT";
var result = digits.ToList(); // 3 items