C# 林克';这是延期执行,但如何执行?

C# 林克';这是延期执行,但如何执行?,c#,.net,linq,lazy-evaluation,C#,.net,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.IsD

这一定很简单。但我还是要问,因为我认为其他人也会为此而挣扎。为什么下面的简单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
的内容,它也不会离开它,因为
数字仍然是
123

那么,为什么LINQ查询不计算新的
输入值,而是始终计算第一个值呢

我知道我可以用这句话来解决它:

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))
{
    // ...
}

您正在为
input
分配一个新值,但是
数字序列仍然是从
input
的初始值派生出来的。换句话说,当您执行
digits=input.Where(Char.IsDigit)
时,它捕获
input
变量的当前值,而不是变量本身。将新值指定给
输入
数字

没有影响区别在于您正在更改
输入
变量的值,而不是该变量所指对象的内容。。。所以
数字
仍然是指原始集合

将其与此代码进行比较:

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());
}
List输入=新列表(Console.ReadLine());
变量数字=输入。其中(Char.IsDigit);//123
while(digits.Any())
{
WriteLine(“输入不包含数字的字符串”);
input.Clear();
input.AddRange(Console.ReadLine());
}

这一次,我们正在修改
input
引用的集合的内容,因为
digits
实际上是该集合的一个视图,所以我们可以看到更改。

digits enumerable是指创建可枚举项时包含的
input
字符串的副本。它不包含对
输入
变量的引用,并且更改
输入
中存储的值不会导致枚举的具体化使用新值

请记住,
其中
是一个静态扩展方法,并接受正在调用它的对象作为参数。

此行:

input.Where(Char.IsDigit)
相当于:

Enumerable.Where(input, Char.IsDigit)
因此,
input
的值被传递为
查询的源,其中
查询不是对
input
的引用


您提出的第一个修复方案之所以有效,是因为它使用了前一行中新分配的
input

我的回答只是为了给其他好的答案增加一个精度,关于延迟执行

即使尚未计算LINQ查询(使用
.Any()
),查询在内部始终引用变量的初始内容。即使在变量受到新的影响后对LINQ查询进行评估,初始内容也不会改变,延迟执行将使用查询始终引用的初始内容:

var input = "ABC123";
var digits = input.Where(Char.IsDigit);
input = "NO DIGIT";
var result = digits.ToList();   // 3 items

这几乎是一个注释,但包含结构化代码,所以我将其作为答案提交

对代码进行以下轻微修改即可:

  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();
Console.WriteLine(“输入某物”);
字符串输入=Console.ReadLine();//例如ABC123
Func anyDigits=()=>input.Any(Char.IsDigit);//将“输入”捕获为字段
while(anyDigits())
{
WriteLine(“输入不包含数字的字符串”);
input=Console.ReadLine();//例如ABC
}
控制台。写线(“再见”);
Console.ReadLine();

这里,
input
被类型为
Func

的委托捕获(闭包)。当您将变量作为参数传递给函数时,它是按值传递的。如此简单的代码,具有如此复杂的副作用。天哪,您从实际的Raymond Chen那里得到了答案™!@s、 m.陈雷蒙的评论和乔恩·斯基特的回答。是的。可以说,
input
是一个ByValue参数,而不是ByRef参数(它没有说
ref
out
)。@JeppeStigNielsen:事实并非如此。字符串是引用类型(这就是为什么可以使用空字符串)。这意味着变量
input
实际上包含对字符串的引用。如果
string
是可变的,则可以将字符串作为普通参数传递给函数,修改其中的字符串也会导致修改原始字符串。但由于您不能修改字符串,所以在某些情况下,它的效果类似于按ByValue传递参数。@tomp我知道
string
是一种引用类型。这不是我想说的。我正在考虑该参数是否是ByRef参数(或者是
ref string
或者
out string
)。所以我们同意。不幸的是,“引用类型”(例如
class
(等)、struct
)和“by-ref-parameter”(
ref
out
)这两个截然不同的概念有着如此相似的名称。这导致了许多误解。事实上,我意识到了这一点,并试图使我的措辞准确,但我还是被误解了…@JeppeStigNielsen:哦,你是对的,当然,我想得太多了。请注意,“初始内容”可能指向可变结构。e、 g.
input=新列表{1};var偶数=input.where(x=>x%2==0);输入。添加(2);var result=偶数.ToList()@NPSF3000:如果它是一个可变集合,我就不会问这个问题,因为没有问题;-)ReSharper抱怨说,第一次使用
输入时,“访问修改的闭包”。建议您更改为
Func
并使用
while(任意数字(输入))
(这可以提高可读性)。@onedaywhen是的!这个wa