尝试自己实现Linq的Count,Where,Select方法。

前言

今天我们来自己尝试实现一下Linq类库中的一些常用的CountWhereSelect方法。通过自己实现这些常用的基础功能,可以加深我们对委托,泛型,以及扩展方法的理解。

正式开始之前呢,还是先简单说一下关于委托泛型以及扩展方法的一些知识。

基础知识

委托

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; }
      /// 
      /// 1:男  2:女
      /// 
      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
    {
        /// 
        /// 自定义Count实现
        /// 
        /// 
        /// 
        /// 
        /// 
        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写法,开动你的脑筋,想想应该怎么写。
尝试自己实现Linq的Count,Where,Select方法。_第1张图片
尝试自己实现Linq的Count,Where,Select方法。_第2张图片
接下来看实现

 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.

欢迎关注微信公众号,一起学习,一起娱乐,一起进步。

你可能感兴趣的:(.NET技术,linq,c#,委托,泛型,C#进阶)