问题描述
我试图更详细地了解 Enumerable.Where
方法的使用。尽管我已经了解了许多细节,包括 lambda 表达式的使用、委托、谓词等,但有些事情对我来说毫无意义,我将不胜感激。
首先我指的是下面链接中的解释:
https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.where?view=net-5.0
在上面的网页中,他们有以下代码示例:
int[] numbers = { 0,30,20,15,90,85,40,75 };
IEnumerable<int> query =
numbers.Where((number,index) => number <= index * 10);
foreach (int number in query)
{
Console.WriteLine(number);
}
/*
This code produces the following output:
0
20
15
40
*/
我的问题是:
-
参数“number”和“index”在哪里定义的?我知道 Where 中的“数字”与 foreach 语句中的“数字”不同。
-
为什么我可以改变
Where
里面的参数“number”的名字,却不能改变“index”的名字? “number
感谢您的关注和支持。
解决方法
参数“number”和“index”在哪里定义的?
它们在您编写 (number,index) => ...
时声明。 (number,index) =>
是 (int number,int index) =>
的缩写。可以省略类型,因为它们可以从 Where
的签名中推断出来。
您正在调用的 Where
的重载是:
public static IEnumerable<TSource> Where<TSource> (
this IEnumerable<TSource> source,Func<TSource,int,bool> predicate
);
numbers
传递给 source
,而 (number,index) => number <= index * 10
传递给 predicate
。在这里可以推断类型,因为根据 source
参数,我们知道 TSource
是 int
(因为您传入了 int[]
),所以 predicate
的类型}} 参数必须是 Func<int,bool>
。
Func<int,bool>
表示一个函数,它接受两个 int
并返回一个 bool
。你应该给 Where
这样的函数。这就是为什么允许您声明两个参数 (number,index)
- 这些是您传递给 Where
的函数的参数。至于功能是做什么的...
左箭头有什么用?
它是“小于或等于”运算符。如果 Where
小于或等于数字索引的 10 倍,则您传递给 number
的函数返回 true。您应该明白为什么在过滤后的序列中只剩下 0(在索引 0)、20(在索引 2)、15(在索引 3)和 40(在索引 6)。这也应该回答你的第三个问题。
为什么我可以改变Where里面的参数“number”的名字,却不能改变“index”的名字?
您可以只重命名index
:
(number,b) => number <= b * 10
甚至重命名它们:
(a,b) => a <= b * 10
它们毕竟只是参数名称。也许你没有做对。
,参数“number”和“index”在哪里定义的?我知道 Where 中的“数字”与 foreach 语句中的“数字”不同。
想象一下代码看起来更像这样:
public bool IsElementValid(int number,int index)
{
return number <= index * 10;
}
IEnumerable<int> query = numbers.Where(IsElementValid);
您的代码 (number,index) => number <= index * 10;
有效地声明了一个匿名方法,该方法接受两个参数:number
和 index
,并返回一个 bool
。这些称为“lambda 表达式”,您可以在 documentation 中阅读有关它们的更多信息。
您可以在此处传递方法,因为 Where
接受 Func<TElement,bool>
delegate。委托有效地允许您在变量中存储一个或多个方法。您可以阅读有关代表 here 的信息。
所以现在我们知道 Func
可以有效地保存一个方法,我们可以通过编写我们自己的方法来消化 Where
是如何工作的:
public List<int> MyOwnWhere(int[] source,Func<int,bool> filter)
{
var result = new List<int>();
for (int i = 0; i < source.Length; ++i)
{
if (filter(source[i],i) == true)
{
result.Add(source[i]);
}
}
return result;
}
当然,这并不是 Where
的工作原理,但您可以了解 Func
背后发生的事情。
我创建了一个示例 here,其中包含一些诊断消息以帮助您了解流程。
为什么我可以改变Where里面的参数“number”的名字,却不能改变“index”的名字?
你可以在不破坏东西的情况下改变它。 Here 我已将它们更改为“bob”和“simon”,但仍然有效。
为什么这段代码会产生输出 0、20、15、40?我知道索引是从 0 到 7。
您的检查是这样执行的:
Index | Check
0 | 0 <= 0 (because 0 * 10 == 0) result = true
1 | 30 <= 10 (because 1 * 10 == 10) result = false
2 | 20 <= 20 (because 2 * 10 == 20) result = true
3 | 15 <= 30 (because 3 * 10 == 30) result = true
4 | 90 <= 40 (because 4 * 10 == 40) result = false
5 | 85 <= 50 (because 5 * 10 == 50) result = false
6 | 40 <= 60 (because 6 * 10 == 60) result = true
7 | 75 <= 70 (because 7 * 10 == 70) result = false
“number
左箭头是 "less than" 的数学符号。与等号结合起来就是“小于或等于”。请参阅 comparison operators 了解更多信息。
,参数“number”和“index”在哪里定义的?我知道 Where 中的“数字”与 foreach 语句中的“数字”不同。
即来自可枚举的扩展方法 where:
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource> (this System.Collections.Generic.IEnumerable<TSource> source,bool> predicate);
这需要一个 Func<source,bool>
(一个函数,它从集合中获取一个源元素、一个 int 索引并返回一个 bool)。
为什么我可以更改Where里面的参数“number”的名称,但不能更改“index”的名称?
表示可枚举中的索引。
为什么这段代码会产生输出 0、20、15、40?我知道索引是从 0 到 7。
{ 0,30,20,15,90,85,40,75 }
当谓词 (number
index 0 number 0: 0 <= 0 * 10 : true
index 1 number 30: 30 <= 1 * 10 : false
index 2 number 20: 20 <= 2 * 10 : true
index 3 number 15: 15 <= 3 * 10 : true
index 4 number 90: 90 <= 4 * 10 : false
index 5 number 85: 85 <= 5 * 10 : false
index 6 number 40: 40 <= 6 * 10 : true
index 7 number 75: 75 <= 7 * 10 : false
“number
数字小于或等于索引乘以十——它小于或等于比较返回一个布尔值。