Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Linq后期绑定混乱_C#_Linq - Fatal编程技术网

C# Linq后期绑定混乱

C# Linq后期绑定混乱,c#,linq,C#,Linq,有人能解释一下我在这里遗漏了什么吗。根据我的基本理解,将在使用结果时计算linq结果,我可以在下面的代码中看到这一点 static void Main(string[] args) { Action<IEnumerable<int>> print = (x) => { foreach (int i in x) { Console.WriteLine(i); }

有人能解释一下我在这里遗漏了什么吗。根据我的基本理解,将在使用结果时计算linq结果,我可以在下面的代码中看到这一点

 static void Main(string[] args)
 {
     Action<IEnumerable<int>> print = (x) =>
     {
         foreach (int i in x)
         {
             Console.WriteLine(i);
         }
     };

     int[] arr = { 1, 2, 3, 4, 5 };
     int cutoff = 1;
     IEnumerable<int> result = arr.Where(x => x < cutoff);
     Console.WriteLine("First Print");
     cutoff = 3;
     print(result);
     Console.WriteLine("Second Print");
     cutoff = 4;
     print(result);
     Console.Read();
}
static void Main(字符串[]args)
{
动作打印=(x)=>
{
foreach(整数i在x中)
{
控制台写入线(i);
}
};
int[]arr={1,2,3,4,5};
int截止=1;
IEnumerable result=arr.Where(x=>x
输出:

First Print 1 2 Second Print 1 2 3 第一次印刷 1. 2. 第二次印刷 1. 2. 3. 现在我改变了主意

arr.Where(x => x < cutoff); 
arr.Where(x=>x

IEnumerable<int> result = arr.Take(cutoff); 
IEnumerable result=arr.Take(截止);
输出如下

First Print 1 Second Print 1 第一次印刷 1. 第二次印刷 1.
为什么使用Take时,它不使用变量的当前值?

Take
不使用lambda,而是一个整数,因此,当您更改原始变量时,它不会更改。

您看到的行为来自于对LINQ函数的参数求值的不同方式。
其中
方法接收lambda,该lambda通过引用捕获值
截止
。它是按需评估的,因此可以看到当时的
截止值

Take
方法(以及类似的方法,如
Skip
)采用
int
参数,因此
cutoff
按值传递。使用的值是调用
Take
方法时
cutoff
的值,而不是在计算查询时


注意:这里的术语延迟绑定有点不正确。后期绑定通常指在运行时与编译时确定表达式绑定到的成员的过程。在C语言中,你可以通过
动态
或反射来实现这一点。LINQ按需评估其部件的行为称为延迟执行

这里有一些不同的东西被弄糊涂了

后期绑定:这是在编译代码后确定代码含义的地方。例如,如果编译器检查
x
类型的对象是否具有
DoStuff()
方法(也考虑扩展方法和默认参数),然后在其输出的代码中生成对它的调用,或者由于编译器错误而失败,则
x.DoStuff()
是早期绑定的。如果在运行时完成对
DoStuff()
方法的搜索,并且如果没有
DoStuff()
方法,则会引发运行时异常,则这是后期绑定。这两种方法各有利弊,C#通常是早期绑定,但支持后期绑定(最简单的方法是通过
动态
,但涉及反射的更复杂的方法也很重要)

延迟执行:严格来说,所有Linq方法都会立即产生结果。但是,该结果是一个对象,它存储对可枚举对象的引用(通常是以前的Linq方法的结果),当它自身被枚举时,它将以适当的方式处理该对象。例如,我们可以编写自己的
Take
方法:

private static IEnumerable<T> TakeHelper<T>(IEnumerable<T> source, int number)
{
  foreach(T item in source)
  {
    yield return item;
    if(--number == 0)
      yield break;
  }
}
public static IEnumerable<T> Take<T>(this IEnumerable<T> source, int number)
{
  if(source == null)
    throw new ArgumentNullException();
  if(number < 0)
    throw new ArgumentOutOfRangeException();
  if(number == 0)
    return Enumerable.Empty<T>();
  return TakeHelper(source, number);
}
捕获的变量:通常,当我们使用变量时,我们会利用其当前状态:

int i = 2;
string s = "abc";
Console.WriteLine(i);
Console.WriteLine(s);
i = 3;
s = "xyz";
这非常直观,它打印的是
2
abc
,而不是
3
xyz
。但是,在匿名函数和lambda表达式中,当我们使用变量时,我们是将其作为变量“捕获”的,因此在调用委托时,我们将使用它的值:

int i = 2;
string s = "abc";
Action λ = () =>
{
  Console.WriteLine(i);
  Console.WriteLine(s);
};
i = 3;
s = "xyz";
λ();
创建
λ
不使用
i
s
的值,而是创建一组指令,说明调用
λ
时如何处理
i
s
。只有在这种情况下,才会使用
i
s
的值

把所有内容放在一起:在您的案例中,没有一个是后期绑定的。那与你的问题无关

在这两方面,你都推迟了执行。对
Take
的调用和对
Where
的调用都返回可枚举对象,这些对象在被枚举时将作用于
arr

只有一个变量中有一个捕获的变量。调用
Take
将一个整数直接传递给
Take
Take
使用该值。调用
,其中
传递从lambda表达式创建的
Func
,该lambda表达式捕获一个
int
变量
Where
对该捕获一无所知,但
Func
知道


这就是为什么这两个人在处理
cutoff

时表现如此不同的原因,这与后期绑定无关……谢谢Jon。我知道后期绑定,但我正在处理多个问题,并在那里使用了错误的术语,而不是延迟执行。但是读你的答案还是很有趣的。谢谢,伙计。在您的
Take
示例实现中,参数异常不会在您指定的位置抛出。您编写的代码在第一次移动到下一次之前都不会运行。您需要有一个外部方法来进行参数验证,并调用一个用yields编写的helper方法。@GideonEngelberth感谢您的帮助。我想要简洁,因为它的目的是阅读而不是运行,但它完全违背了我的写作要点。仔细看一下真实情况,我意识到它应该把负数当作零,但我想我会把这个错误留在那里,因为它不会挫败我的论点。
int i = 2;
string s = "abc";
Action λ = () =>
{
  Console.WriteLine(i);
  Console.WriteLine(s);
};
i = 3;
s = "xyz";
λ();