一.数据准备
public class Student
{
    public string Name { get; set; }

    public int Age { get; set; }
}

var studentList = new List<Student>();
Student student1 = new Student();
student1.Name = "张三";
student1.Age = 30;

Student student2 = new Student();
student2.Name = "李四";
student2.Age = 29;

Student student3 = new Student();
student3.Name = "赵春来";
student3.Age = 28;

studentList.Add(student1);
studentList.Add(student2);
studentList.Add(student3);

 

二.看看Where是什么?
studentList.Where是接受泛型继承IEnumerable类型的数据源,和接受泛型类型返回布尔值的委托,返回IEnumerable类型的扩展方法

 

 

三.常规写法

a.foreash 遍历集合得写两遍
b.添加到结果集合得写两遍
不同的是查询条件不同

//查找年龄小于30的学生
{
    var list = new List<Student>();
    foreach (var item in studentList)
    {
        if (item.Age < 30)
        {
            list.Add(item);
        }
    }
}
//查找姓名大于2个字的学生
{
    var list = new List<Student>();
    foreach (var item in studentList)
    {
        if (item.Name.Length > 2)
        {
            list.Add(item);
        }
    }
}

 

四.第一次封装Linq To Object

a.把集合传递进去
b.把查询条件传递进去--返回布尔值的委托

//1、调用第一次封装Linq to Object
CustomerWhereOne(studentList, ((s) => s.Age < 30));
CustomerWhereOne(studentList, new Func<Student, bool>(s => s.Name.Length > 2));

/// <summary>
/// 1、第一次封装Linq To Object
/// </summary>
/// <param name="resource">数据源</param>
/// <param name="func">返回布尔值的委托</param>
public static List<Student> CustomerWhereOne(List<Student> resource, Func<Student, bool> func)
{
    var result = new List<Student>();
    foreach (var item in resource)
    {
        if (func(item))
        {
            result.Add(item);
        }
    }

    return result;
}

五.第二次封装Linq To Object
a.返回值和数据源改为泛型--List<T>

b.改为扩展方法

//2、调用第二次封装Linq To Object
{
    var resultList = studentList.CustomerWhereTwo((s) => s.Age < 30);
    var resultList2 = studentList.CustomerWhereTwo(new Func<Student, bool>(s => s.Name.Length > 2));

    foreach (var item in resultList)
    {
        Console.WriteLine(item.Name);
    }
}

/// <summary>
/// 2、第二次封装Linq To Object,改为泛型,改为扩展方法
/// </summary>
/// <param name="resource">数据源</param>
/// <param name="func">返回布尔值的委托</param>
public static List<T> CustomerWhereTwo<T>(this List<T> resource, Func<T, bool> func)
{
    var result = new List<T>();
    foreach (var item in resource)
    {
        if (func(item))
        {
            result.Add(item);
        }
    }

    return result;
}

六.第三次封装Linq To Object,改为yield迭代器模式,完成了数据的按需获取,延迟加载

yield和IEnumerable配合使用
a.返回值类型改为IEnumerable
b.return前面加yield

//3、调用第三次封装Linq to Object
{
    var resultList = studentList.CustomerWhereThree((s) => s.Age < 30);
    var resultList2 = studentList.CustomerWhereThree(new Func<Student, bool>(s => s.Name.Length > 2));
    foreach (var item in resultList)
    {
        Console.WriteLine(item.Name);
    }
}

/// <summary>
/// 3、第三次封装Linq To Object,改为迭代器模式,完成了数据的按需获取,延迟加载
/// </summary>
/// <param name="resource">数据源</param>
/// <param name="func">返回布尔值的委托</param>
public static IEnumerable<T> CustomerWhereThree<T>(this IEnumerable<T> resource, Func<T, bool> func)
{
    var result = new List<T>();
    foreach (var item in resource)
    {
        if (func(item))
        {
            yield return item;
        }
    }
}

调试代码,我们发现resultList返回的是个ResultView,且提示当展开结果视图枚举IEnumerable

只有当真正使用,去遍历的时候才去调用真正去调用CustomerWhereThree

至此我们通过一步步封装了一个和内置Where一样的CustomerWhereThree方法--Linq To Object

总结

1.Linq是基于委托的封装(例子中的--func),逻辑解耦(查询条件可变化的部分传递进去 --s.Age < 30和s.Name.Length > 2),代码重用(把循环遍历集合和添加到结果集这样的动作封装了起来--foreach (var item in resource))。

2.IEnumerable-yield-利用的迭代器的延迟,按需获取。