C# 林克';这是延期执行,但如何执行?
这一定很简单。但我还是要问,因为我认为其他人也会为此而挣扎。为什么下面的简单LINQ查询不总是使用新变量值而不是总是使用第一个变量值执行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
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