繁体   English   中英

LINQ的延期执行,但如何?

[英]LINQ's deferred execution, but how?

这一定非常简单。 但无论如何我会问它,因为我认为其他人也会挣扎。 为什么以下简单的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))
{
    // ...
}

区别在于您正在更改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实际上是对该集合的视图,因此我们可以看到更改。

您正在为input分配新值,但digits序列仍然是从input的初始值派生的。 换句话说,当你执行digits = input.Where(Char.IsDigit)它会捕获 input变量的当前值 ,而不是变量本身。 input分配新值对digits没有影响。

这一行:

input.Where(Char.IsDigit)

相当于:

Enumerable.Where(input, Char.IsDigit)

因此, input的值作为.Where查询的源传递,而不是对input引用

您建议的第一个修复工作是因为它使用先前行上新input值。

枚举中的数字指的是字符串的副本input包含在创建枚举。 它不包含对input变量的引用,并且更改存储在input的值不会导致可枚举的实现使用新值。

请记住, Where是静态扩展方法,并接受您将其作为参数调用的对象。

这几乎是一个评论,但包含结构化代码,所以我提交它作为答案。

以下对代码的轻微修改将起作用:

  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();

这里inputFunc<bool>类型的委托捕获(闭包)。

我正在回答只是为其他好的答案添加精度,关于延迟执行

即使尚未评估LINQ查询(使用.Any() ),查询内部也总是引用变量初始内容 即使在对变量影响了新内容之后评估LINQ查询,初始内容也不会更改,延迟执行将使用查询一直引用的初始内容:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM