前言
今天我们来自己尝试实现一下Linq
类库中的一些常用的Count
,Where
,Select
方法。通过自己实现这些常用的基础功能,可以加深我们对委托,泛型,以及扩展方法的理解。
正式开始之前呢,还是先简单说一下关于委托
,泛型
以及扩展方法
的一些知识。
基础知识
委托
C#中普通方法接受的是参数可以是类,可以是变量。而委托接受的是方法,可以理解为委托是可以指向方法的类型,把一个方法当做变量进行传递。 C#默认提供了两种委托方法,一共是Action
委托没有返回值,另一种是Func
委托有返回值。
泛型
泛型表示不知道传入的参数是哪一种类型,它会在使用的时候自动进行处理,泛型一般用T表示,如果有多个泛型,可以接着用T1、T2、T3等表示。
扩展方法
有时候可以需要给一些类库添加自定义的扩展方法,那么最好保持命名空间和原始的类库一致,并且在第一个入参里添加this
关键字。
开始实战
首先我们新建一个控制台项目,然后新增一个类,添加一个名为Employee
的员工实体,并且重写ToString
方法方便打印数据。
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
/// <summary>
/// 1:男 2:女
/// </summary>
public int Gender { get; set; }
public int Salary { get; set; }
public override string ToString()
{
return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";
}
}
接着在Program
类下的Main
方法里随便添加一些数据。
var employees = new List<Employee>() {
new Employee(){ Id=1,Name="大虎",Age=28,Gender=1,Salary=8000},
new Employee(){ Id=2,Name="翠花",Age=29,Gender=2,Salary=9000},
new Employee(){ Id=3,Name="大壮",Age=30,Gender=1,Salary=10000},
new Employee(){ Id=4,Name="铁柱",Age=31,Gender=1,Salary=11000},
new Employee(){ Id=5,Name="阿强",Age=32,Gender=1,Salary=12000},
new Employee(){ Id=6,Name="阿秀",Age=33,Gender=2,Salary=13000},
new Employee(){ Id=7,Name="柱子",Age=40,Gender=1,Salary=14000},
};
到目前准备工作就算做好了,接下来开始提需求。
需求1:使用
Count
方法统计女性员工人数。
那么传统的Linq写法如下。
var count = employees.Count(c => c.Gender==2);
Console.WriteLine($"女性员工数:{count}");
接下来为了实现自定义Linq
的扩展方法,我们在新建一个LinqExtension
的扩展类。然后添加一个MyCount
方法
using System;
using System.Collections.Generic;
namespace 自定义Linq方法
{
public static class LinqExtension
{
/// <summary>
/// 自定义Count实现
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="func"></param>
/// <returns></returns>
public static int MyCount<T>(this IEnumerable<T> items, Func<T, bool> func)
{
int count = 0;
foreach (var item in items)
{
if (func(item))
{
count++;
}
}
return count;
}
}
}
调用方式如下。
var count = employees.MyCount(e => e.Gender==2);
Console.WriteLine($"女性员工数:{count}");
这样看起来,和原始的Linq
的使用方法一样。在MyCount
方法中,第一个入参需要使用this
关键字,表明这是一个扩展方法,然后使用Func
委托,其中Func
委托中的bool
表示返回值类型,所以表达式e => e.Gender==2
时,返回值是bool
类型。然后我们就可以统计结果了。
需求2:使用
Where
方法输出Salary大于等于10000的员工信息。
传统Linq写法如下。
var newEmployees = employees.Where(c => c.Salary>=10000);
foreach (var item in newEmployees)
{
Console.WriteLine(item);
}
自定义MyWhere
方法如下。
public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> items, Func<T, bool> func)
{
foreach (var item in items)
{
if (func(item))
{
yield return item;
}
}
}
调用方式和传统Linq一致。
var newEmployees = employees.MyWhere(c => c.Salary>=10000);
foreach (var item in newEmployees)
{
Console.WriteLine(item);
}
在这里我们使用yield
关键字,目的是可以实现延迟执行,当然也可以用如下的写法。
public static IEnumerable<T> MyWhere1<T>(this IEnumerable<T> items, Func<T, bool> func)
{
var result = new List<T>();
foreach (var item in items)
{
if (func(item))
{
result.Add(item);
}
}
return result;
}
使用List
这种方式,相当于是一次性把foreach
中的所有数据处理完然后在统一返回,但是添加yield
关键字后发现,它是处理一条返回一条。当然两种方式都是可以的,不过参考Linq
的源码发现,推荐使用yield
关键字。
需求3:使用
Select
方法输出一个新对象,查询所有员工的姓名和年龄。
这里我们需要在新建一个实体。仅包括姓名和年龄。
public class User
{
public string Name { get; set; }
public int Age{ get; set; }
public override string ToString()
{
return $"Name={Name},Age={Age}";
}
}
传统Linq写法。
var users = employees.Select(c => new User() { Name = c.Name, Age=c.Age }).ToList();
foreach (var item in users)
{
Console.WriteLine(item);
}
自定义Select写法,开动你的脑筋,想想应该怎么写。
接下来看实现
public static IEnumerable<T2> MySelect<T1, T2>(this IEnumerable<T1> items, Func<T1, T2> func)
{
foreach (var item in items)
{
yield return func(item);
}
}
这里只需要明白传入的是两个泛型T1,T2,然后返回值是T2,基本就能明白了。
Study hard and make progress every day.
欢迎关注微信公众号,一起学习,一起娱乐,一起进步。