Linq知识整理----Where,Select

本文详细介绍了Linq中Where和Select的使用和实现原理。通过示例展示了如何使用yield return生成IEnumerable序列,以及Where方法的泛型扩展实现。同时,解释了Select方法如何进行映射操作,以及泛型方法的应用,使代码更具通用性。文章最后总结了Where的过滤作用和Select的映射功能,并强调了lambda表达式在Linq中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值