1.使用方法创建IEnumerable,IEnumerable不是数组类型,为可枚举类型
public static IEnumerable<string> GenerateSequence() {
var i = 0;
while (i++ < 100)
{
yield return i.ToString();
}
yield return 与 return的区别是,yield return 是一个一个传递过去,return是整个产生了才传过去,这里如果i++<int.Maxvalue,
主程序只打印到100的时候,由于return是要产生整个int,程序会一直进行产生int的过程。yield return 会产生一个,返回一个,所以不会一直进行int的产生,只在有需要的时候产生。
2.实现Linq的Where方法
①调用GenerateSequence生成序列,并且打印生成的序列
var sequence = GenerateSequence();
foreach (var item in sequence)
{
Console.WriteLine(item.ToString());
}
②使用Where的方法来打印长度小于2的数字
sequence = sequence.Where(s => s.Length < 2);
foreach (var item in sequence)
{
Console.WriteLine(item.ToString());
}
这里Where的方法声明为:
public static IEnumerable Where(this IEnumerable source, Func<TSource, bool> predicate);
是一个泛型的扩展方法,第一个参数是告诉编译器,这是关于IEnumerable的扩展方法,第二个参数是要传进去一个方法,方法为有一个泛型参数,并且返回一个布尔值
分析完之后,这里我们自己来创建这个扩展方法,首先声明一个静态类(扩展方法都是放在静态类里面的)
③声明静态类MyLinqImplementatiom
public static class MyLinqImplementatiom {
}
④实现扩展方法Where
public static class MyLinqImplementatiom {
public static IEnumerable<string> Where(this IEnumerable<string> source) {
foreach (string item in source)
{
if (item.Length<2)
{
yield return item;
}
}
}
}
这时候调用Where方法
sequence = sequence.Where();
foreach (var item in sequence)
{
Console.WriteLine(item.ToString());
}
也会实现②中的效果,但是这个Where可重用性很差,因为这个时候的筛选是不可改变的
⑤所以给这个扩展方法Where添加一个Func<string,bool>有一个字符参数,返回值为bool类型的委托变量,这个时候静态方法就改成了
public static class MyLinqImplementatiom {
public static IEnumerable<string> Where(this IEnumerable<string> source,Func<string,bool> predicate) {
foreach (string item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
}
调用Where的时候,就可以传进去一个参数为一个string类型,返回值类型为bool类型的方法了
sequence = sequence.Where(s=>s.Length<2);
看上去s没有被定义类型,可是实际上编译器已经定义其为string类型了。这个Where虽然已经可以自定义筛选条件了,可是只能处理字符类型的,为了不让代码爆炸,这时候可以使用泛型方法,即把扩展方法中的string换成T。并且在Where后面加上,来告诉编译器这是一个泛型方法。
public static class MyLinqImplementatiom {
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,Func<T,bool> predicate) {
foreach (T item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
}
⑥添加一个int序列来测试,筛选条件改为被3整除的数字
private static IEnumerable<int> GenerateNumbers() {
var i = 0;
while (i++ < 100)
yield return i;
}
var sequence2 = GenerateNumbers().Where(n => n % 3 == 0);
foreach (var item in sequence2)
{
Console.WriteLine(item.ToString());
}
3.实现linq中的select
①看默认select的方法声明
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector);
可以看到有两个版本的select,从参数可以看出,select也是一个扩展方法,传进去的委托类型为参数为泛型,返回值也为泛型的委托
②来试着使用下,调用生成数字的GenerateNumbers(),然后使用select把int的类型转为string类型,并让打印结果距离左边20个单位
var sequence = GenerateNumbers().Select(n => n.ToString().PadLeft(20));
foreach (var item in sequence)
{
Console.WriteLine(item);
}
可以看到select可以将某些内容映射成完全不同的内容,像是将一个数字型的3,变成字符型的"3"。
③在静态类MyLinqImplementatiom 中声明静态方法Select,第一个参数和扩展方法的声明一样,第二个参数为Func<int,string>,参数为int类型返回值类型为string类型的委托
public static IEnumerable<string>
Select(this IEnumerable<int> source, Func<int, string> selector) {
foreach (int item in source)
{
yield return selector(item);
}
}
这个方法的可重用性也不高,因为只可以处理int类型,并且只可以返回string类型,所以希望处理任何类型的参数,返回我所需要的类型的IEnumerable
④使用泛型
返回值的IEnumerable变为IEnumerable,这样就可以返回我所需要的类型的IEnumerable,接下来修改Select中的参数,因为希望处理所有类型的参数,所以第一个IEnumerable改为
IEnumerable,第二个Func<int,string>,int改为我IEnumerable的类型,string改为我要返回的类型,所以变为Func<TSource,TResult>,并告诉编译器这是一个泛型的方法,将Select改为Select<TSource,TResult>
public static IEnumerable<TResult>
Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) {
foreach (var item in source)
{
yield return selector(item);
}
}
所以会返回什么样的,或者select之后映射成什么样子,就看传入的selector,为传入一个类型,返回一个类型的泛型方法
⑤select的重载,selector纯进去的参数除了一个要处理的类型外,还有一个int类型的index,这里要修改的就是Func了重载的方法就改为
Select<TSource, TResult>(this IEnumerable source, Func<TSource, int,TResult> selector){}
即要修改selector部分
public static IEnumerable<TResult>
Select<TSource,TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector) {
int index=0;
foreach (var item in source)
{
yield return selector(item,index++);
}
}
总结:
Filter:会生成序列的子集,实现的原理是设置一些条件,判断是否为true,Linq中的where(一个参数为T,返回值为bool类型的方法)
Map:为每个现有项生成一个新项,Linq中的select(参数为T,返回值为T的方法,根据需要判断是否需要index)
使用lambda当作参数会让代码看起来更加整洁
sequence.where(n=>n>3)//传入一个T,返回一个bool
sequence.select(n=>n)//传入一个T,返回一个T