5:Lambda表达式与Linq与匿名类扩展方法详解

前言

代码较多,直接搬老师的,但是感觉很有用,都是和标题密切相关的内容。要全部看完哟!
PS:部分老师打的广告去掉了,嘿嘿!

Lambda前世今生

在.NetFramewok的不同版本都有不同的样子;在.NetCore下也都是支持的;

接下来代码展示:

public class LambdaShow
{
    
    
    public delegate void NoReturnNoPara();
    public delegate void NoReturnWithPara(int x, string y);//1 声明委托
    public delegate int WithReturnNoPara();
    public delegate string WithReturnWithPara(out int x, ref int y);

    public void Show()
    {
    
    
        //Lambda前世今生;
        {
    
    
            //.Netframework1.0/1.1时代
            NoReturnNoPara method = new NoReturnNoPara(DoNothing);
            NoReturnWithPara method1 = new NoReturnWithPara(Study);
            method1.Invoke(123, "Richard");
        }
        //.NetFramework2.0  匿名方法  增加了一个delegate关键字,可以访问到除了参数以外的局部变量
        int i = 0;
        {
    
    
            NoReturnWithPara method = new NoReturnWithPara(delegate (int id, string name)
            {
    
    
                Console.WriteLine($"{id} {name} 学习.Net高级班");
                Console.WriteLine(i);
            });
            method.Invoke(123, "Richard");
        }
        //.NetFramework3.0
        {
    
    
            //.NetFramework3.0 去掉了delegate关键字,添加了一个=>  goes to
            NoReturnWithPara method = new NoReturnWithPara((int id, string name) =>
            {
    
    
                Console.WriteLine($"{id} {name} 学习.Net高级班");
                Console.WriteLine(i);
            });
            method.Invoke(123, "Richard");

        }
        {
    
    
            //.NetFramework3.0 后期,去掉了匿名方法红的参数类型
            NoReturnWithPara method = new NoReturnWithPara((id, name) =>  //编译器自动推断来的/编译器提供的便捷功能(语法糖)
            {
    
    
                Console.WriteLine($"{id} {name} 学习.Net高级班");
                Console.WriteLine(i);
            });
            method.Invoke(123, "Richard");
        }
        {
    
    
            //如果匿名方法体中只有一行代码,可以省略方法体的大括号;
            NoReturnWithPara method = new NoReturnWithPara((id, name) => Console.WriteLine($"{id} {name} 学习.Net高级班")
            );
            method.Invoke(123, "Richard");
        }
        {
    
    
            NoReturnWithPara method = (id, name) => Console.WriteLine($"{id} {name} 学习.Net高级班");
            method.Invoke(123, "Richard");
        }
        {
    
    
            //一个参数的时候;
            Action<string> method = s => Console.WriteLine("欢迎大家来到.Net高级班进阶学习");
            method.Invoke("牧羊人。。");
        }
        {
    
    
            //如果有返回值? 如果lambda表达式中只有一行代码,且有返回值,可以省略return;
            Func<string> func = () => "黄大仙";
            Func<int, string> func1 = i => i.ToString();
        }
        //大家觉得Lambda表达式本质是什么?
        //多播委托中是不能把Lambda表达式-=;因为Lambda表达式其实是一个方法,不同的lambda表达式就是不同的方法; 
        //lambda的本质是一个方法;  
       // 语法糖:编译器提供的便捷功能;
    }

    private void DoNothing()
    {
    
    
        Console.WriteLine("This is DoNothing");
    }

    private void Study(int id, string name)
    {
    
    
        //Console.WriteLine(i);
        Console.WriteLine($"{id} {name} 学习.Net高级班");
    }
}

匿名类

匿名类就是声明的时候以var类型来标识。系统会根据声明右侧的数据类型来推断数据类型。
#region 3.0出了个匿名类  
{
    
    
    Console.WriteLine("*****************匿名类**************");
    //正常情况声明类
    Student student = new Student()
    {
    
    
        Id = 1,
        Name = "Nochat",
        Age = 25,
        ClassId = 2
    };
    student.Study();

    Console.WriteLine(student.Id);
    Console.WriteLine(student.Name);

    //匿名类object
    object model = new//3.0    
    {
    
    
        Id = 1,
        Name = "Nochat",
        Age = 25,
        ClassId = 2
    };

    //无法访问属性值
    //Console.WriteLine(model.Id); //为什么? 因为Object没有这个属性;因为C#是强类型语言,object是在编译时确定类型额; 
    //Console.WriteLine(model.Name);

    //dynamic避开编译器检查 (4.0)
    //(动态类型),可以避开编译器的检查
    dynamic dModel = new//  dynamic可以便可编译器的检查
    {
    
    
        Id = 1,
        Name = "Nochat",
        Age = 25,
        ClassId = 2
    };

    dModel.Id = 134;
    Console.WriteLine(dModel.Id);
    Console.WriteLine(dModel.Name);
    //Console.WriteLine(dModel.Js);// 会报异常

    //var 语法糖   可以理解为弱类型;不确定类型;
    Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    var model1 = new //不能声明方法
    {
    
    
        Id = 1,
        Name = "Nochat",
        Age = 25,
        ClassId = 2
    };
    Console.WriteLine(model1.Id); //var 定义的匿名类,可以访问存在的属性值;
    Console.WriteLine(model1.Name);
    //Console.WriteLine(model1.Js); 
    //  model1.Id = 3; // 属性是只读,不能设置值;

    Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    int i2 = 2;
    var i1 = 1;//var就是个语法糖,由编译器自动推算  
    var s = "Richard";

    //var strnull = null; //编译器不允许;

    // var aa;//var声明的变量必须初始化,必须能推算出类型
    //也不允许作为方法的参数类型

    1 var 配合匿名类型使用
    2 var 偷懒,复杂类型的使用 
    3 在不知道具体是什么类型的时候就可以使用var 来声明;

    //缺陷:个人认为,在代码阅读的时候不是很方便;
    //建议大家在写代码的时候,尽量明确类型;

    var i = 13;
    var j = "";
    var mode = new {
    
     };

}

扩展方法

首先建立一个学生类

里边有参数有方法。
public class Student
    {
    
    
        public int Id {
    
     get; set; }
        public int ClassId {
    
     get; set; }
        public string Name {
    
     get; set; }
        public int Age {
    
     get; set; }

        private string Decription {
    
     get; set; }

        public void Study()
        {
    
    
            Console.WriteLine("{0} {1}跟着A老师学习.net高级开发", this.Id, this.Name);
        }

        public void StudyFramework()
        {
    
    
            Console.WriteLine("{0} {1}跟着B老师学习如何做一名架构师。。。", this.Id, this.Name);
        }

        public void StudyFullStack()
        {
    
    
            Console.WriteLine("{0} {1}跟着C老师学习全栈开发。。。", this.Id, this.Name);
        }

    }

    /// <summary>
    /// 班级实体
    /// </summary>
    public class Class
    {
    
    
        public int Id {
    
     get; set; }
        public string ClassName {
    
     get; set; }
    }

现在又有一个老师开了新课

咱们需要对student进行方法添加。1:修改student;2:添加扩展方法。
{
    
    
                    Student student = new Student()
                    {
    
    
                        Id = 1,
                        Name = "Mr.zhang",
                        Age = 25,
                        ClassId = 2
                    };

                    //如果要增加一个方法---和C老师一起学习全栈开发;
                    //给老师支个招:
                    //最直接的方法:就只直接增加一个方法;
                    //增加了方法,就修改了这个Student类型,修改了这个类,一定需要重新测试;就会导致代码不稳定;

                    //没有遵循开闭原则: 面向扩展开发,面向修改关闭;
                    //直接增加方法,其实就是修改了类,修改类,类就不稳定;
                    //正确的姿势:应该是不修改类的前提下,可以做到增加功能、这才是真正的扩展之道;
                    student.Study();
                    student.StudyFramework();
                    student.StudyFullStack();

                    MethodExtension.StudyFullStack(student);
                    student.StudyFullStack();  //就和调用实例方法是一样的; 这就是真正的扩展之道

                    //张三:直接在Student类中增加了一个方法;
                    //李四:写一个扩展方法
                    //二者会冲突吗? 虽然不会冲突,但是还是会以实例方法为准;
                    //建议:建议大家以后如果需要新增功能,尽量不要在之前的基础上修改,而是通过扩展来,只要是标准统一,就不会出现以上情况;

                    int i = 1;
                    int? j = 2;
                    //int x = i + j;  //类型不一样;
                    int x = i + j.ToInt();

                    string sResult = "吧啦吧啦VIP课程学习完毕以后, 老师希望大家多多money";
                    // 朝夕教育高级班VIP课程.......
                    sResult.ToStringLength();

                    //能不能扩展一下Object,扩展了Object以后,任何一个类型都可以来调用;因为Objectct是所有类型的父类----扩展方法是可以被继承的; 
                    //1. 扩展Object 类型以后,会造成类型的方法污染;
                    //2. 除非需求很明确,否则不要随意的扩展Object或者没有约束的泛型;
                    //i.ObjExtend()
                    string s = "";
                    s.ObjExtend();
                    student.ObjExtend();

                    s.GenericExtend();
                    student.GenericExtend();
                    i.GenericExtend();
                }

扩展方法的添加

/// 扩展方法:
/// 1.增加一个静态类
/// 2.定义一个静态方法 把需要扩展的类传递过来
/// 3.在新增的方法中的第一个参数 标记一个this 关键字
    public static class MethodExtension
    {
    
    
        /// <summary>
        /// 扩展方法其实是新增的一个方法
        /// </summary>
        /// <param name="student"></param>
        public static void StudyFullStack(this Student student)
        {
    
    
            //student.Decription;
            Console.WriteLine("{0} {1}跟着C老师学习全栈开发。。。", student.Id, student.Name);
        }

        public static int ToInt(this int? i)
        {
    
     
            return i ?? 0; //这里??的意思是i是否为空,为空返回0,否则返回i
            //if (i == null)
            //{
    
    
            //    return 0;
            //}
            //else
            //{
    
    
            //    return i.Value;
            //}
        }

        /// <summary>
        /// /截取字符串扩展
        /// </summary>
        /// <param name="str"></param>
        /// <param name="length"></param>
        /// <returns></returns>

        public static string ToStringLength(this string str,int length=10)
        {
    
    
            if (string.IsNullOrWhiteSpace(str))
            {
    
    
                return string.Empty;
            }
            else if (str.Length>length)
            {
    
    
                return $"{str.Substring(0, length)}.....";
            }
            else
            {
    
    
                return str;
            }
        }


        public static string ObjExtend(this object obj)
        {
    
    
            if (obj == null)
            {
    
    
                return string.Empty;
            }

            //.......
            //else if (true)
            //{
    
    

            //}
            else
            {
    
    
                return obj.ToString();
            }
        }

        public static string GenericExtend<T>(this T t)
        {
    
    
            if (t == null)
            {
    
    
                return string.Empty;
            }

            //.......
            //else if (true)
            //{
    
    

            //}
            else
            {
    
    
                return t.ToString();
            }
        }
    }
    //如果要扩展属性或者扩展字段:可以通过继承来实现;

Linq

在这里要说一下Linq和Lambda的区别
Linq:是一个扩展方法。
Lambda:是一个匿名方法。
/// <summary>
    /// Linq To Object
    /// .NetFramework3.0的一个非常重大的改变
    /// </summary>
    public class LinqShow
    {
    
    
        #region Data Init
        private List<Student> GetStudentList()
        {
    
    
            #region 初始化数据
            List<Student> studentList = new List<Student>()
            {
    
    
                new Student()
                {
    
    
                    Id=1,
                    Name="赵亮",
                    ClassId=2,
                    Age=35
                },
                new Student()
                {
    
    
                    Id=1,
                    Name="再努力一点",
                    ClassId=2,
                    Age=23
                },
                 new Student()
                {
    
    
                    Id=1,
                    Name="王炸",
                    ClassId=2,
                    Age=27
                },
                 new Student()
                {
    
    
                    Id=1,
                    Name="疯子科学家",
                    ClassId=2,
                    Age=26
                }
            };
            #endregion
            return studentList;
        }
        #endregion

        public void Show()
        {
    
    
            List<Student> studentList = this.GetStudentList();
            //常规情况下数据过滤
            //1.要求查询Student中年龄小于30的;
            {
    
    
                var list = new List<Student>();
                foreach (var item in studentList)
                {
    
    
                    if (item.Age < 30)
                    {
    
    
                        list.Add(item);
                    }
                }
                //循环完毕以后,list里面必然是符合要求的数据; 
                //Func<Student, bool> func = item => item.Age < 30; 
                var list1 = MethodExtension.RichardWhere(studentList, item => item.Age < 30);
                var list2 = studentList.RichardWhere(item => item.Age < 30);

            }
            //2.要求Student名称长度大于2
            {
    
    
                //Name长度大于2的
                var list = new List<Student>();
                foreach (var item in studentList)
                {
    
    
                    if (item.Name.Length > 2)
                    {
    
    
                        list.Add(item);
                    }
                }
                var list1 = MethodExtension.RichardWhere(studentList, item => item.Name.Length > 2);
                var list2 = studentList.RichardWhere(item => item.Name.Length > 2);
                //循环完毕以后,list里面必然是符合要求的数据;
            }
            //3. N个条件叠加
            {
    
    
                //N个条件叠加
                var list = new List<Student>();
                foreach (var item in studentList)
                {
    
    
                    if (item.Id > 1
                        && item.Name != null
                        && item.ClassId == 1
                        && item.Age > 20)
                    {
    
    
                        list.Add(item);
                    }
                }
                var list1 = MethodExtension.RichardWhere(studentList, item => item.Id > 1
                        && item.Name != null
                        && item.ClassId == 1
                        && item.Age > 20);
                //循环完毕以后,list里面必然是符合要求的数据;
                var list2 = studentList.RichardWhere<Student>(item => item.Id > 1
                        && item.Name != null
                        && item.ClassId == 1
                        && item.Age > 20);
            } 
            //思考一下:如果来一堆Teache,那我要过滤这个Teacher 怎么办呢?
            //需要共用行,需要一个方法满足不同类型的需求---那就是泛型方法;

            ///1.为什么不喜欢前面的这种循环呢? 不好看,代码量多--其实代码多不是事儿---封装一下;
            ///2.随着条件的变更,每一次都需要来写一个循环;都是循环---主要问题是:相同代码太多了;

            #region Linq 扩展方法&表达式
            {
    
    
                var list = studentList.Where<Student>(s => s.Age < 30); //list里面必然是符合要求的数据;
                foreach (var item in list)
                {
    
    
                    Console.WriteLine("Name={0}  Age={1}", item.Name, item.Age);
                }
            }
            {
    
    
                Console.WriteLine("********************");
                var list = from s in studentList
                           where s.Age < 30
                           select s;   //list里面必然是符合要求的数据;

                foreach (var item in list)
                {
    
    
                    Console.WriteLine("Name={0}  Age={1}", item.Name, item.Age);
                }
            }
            #endregion

            #region linq to object Show
            {
    
    
                Console.WriteLine("********************");
                var list = studentList.Where<Student>(s => s.Age < 30)
                                     .Select(s => new
                                     {
    
    
                                         IdName = s.Id + s.Name,
                                         ClassName = s.ClassId == 2 ? "高级班" : "其他班"
                                     });
                foreach (var item in list)
                {
    
    
                    Console.WriteLine("Name={0}  Age={1}", item.ClassName, item.IdName);
                }
            }
            {
    
    
                Console.WriteLine("********************");
                var list = from s in studentList
                           where s.Age < 30
                           select new
                           {
    
    
                               IdName = s.Id + s.Name,
                               ClassName = s.ClassId == 2 ? "高级班" : "其他班"
                           };

                foreach (var item in list)
                {
    
    
                    Console.WriteLine("Name={0}  Age={1}", item.ClassName, item.IdName);
                }
            }
            {
    
    
                Console.WriteLine("********************");
                var list = studentList.Where<Student>(s => s.Age < 30)//条件过滤
                                     .Select(s => new//投影
                                     {
    
    
                                         Id = s.Id,
                                         ClassId = s.ClassId,
                                         IdName = s.Id + s.Name,
                                         ClassName = s.ClassId == 2 ? "高级班" : "其他班"
                                     })
                                     .OrderBy(s => s.Id)//排序
                                     .OrderByDescending(s => s.ClassId)//倒排
                                     .Skip(2)//跳过几条
                                     .Take(3)//获取几条
                                     ;
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"Name={item.ClassName}  Age={item.IdName}");
                }
            }
            {
    
    //group by
                Console.WriteLine("********************");
                var list = from s in studentList
                           where s.Age < 30
                           group s by s.ClassId into sg
                           select new
                           {
    
    
                               key = sg.Key,
                               maxAge = sg.Max(t => t.Age)
                           };
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"key={item.key}  maxAge={item.maxAge}");
                }
                //group by new {s.ClassId,s.Age}
                //group by new {A=s.ClassId>1}
            }
            {
    
    
                Console.WriteLine("********************");
                var list = studentList.GroupBy(s => s.ClassId).Select(sg => new
                {
    
    
                    key = sg.Key,
                    maxAge = sg.Max(t => t.Age)
                });
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"key={item.key}  maxAge={item.maxAge}");
                }
            }
            List<Class> classList = new List<Class>()
                {
    
    
                    new Class()
                    {
    
    
                        Id=1,
                        ClassName="初级班"
                    },
                    new Class()
                    {
    
    
                        Id=2,
                        ClassName="高级班"
                    },
                    new Class()
                    {
    
    
                        Id=3,
                        ClassName="微信小程序"
                    },
                };
            {
    
    
                var list = from s in studentList
                           join c in classList on s.ClassId equals c.Id
                           select new
                           {
    
    
                               Name = s.Name,
                               CalssName = c.ClassName
                           };
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
                }
            }
            {
    
    
                var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
                {
    
    
                    Name = s.Name,
                    CalssName = c.ClassName
                });
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
                }
            }
            {
    
    //左连接
                var list = from s in studentList
                           join c in classList on s.ClassId equals c.Id
                           into scList
                           from sc in scList.DefaultIfEmpty()//
                           select new
                           {
    
    
                               Name = s.Name,
                               CalssName = sc == null ? "无班级" : sc.ClassName//c变sc,为空则用
                           };
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
                }
                Console.WriteLine(list.Count());
            }
            {
    
    
                var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
                {
    
    
                    Name = s.Name,
                    CalssName = c.ClassName
                }).DefaultIfEmpty();//为空就没有了
                foreach (var item in list)
                {
    
    
                    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
                }
                Console.WriteLine(list.Count());
            }
            {
    
    

            }
            #endregion
        }

    }
接下来编写扩展方法中的内容。用来实现跟Lambda中一样的功能。也是上述类中所调用的。
public static class MethodExtension
    {
    
    
        /// <summary>
        /// 这就是Linq中where的本质;
        /// 可以让开发者只需要关注自己的核心业务,其他别都以Linq封装;
        /// 
        /// Linq---Linq To Object:就是针对于IEnumerable类型数据; 
        /// IEnumerable类型数据:可以理解成内存中的数据;
        /// 1.其实就是把不变的逻辑封装起来,把可变的逻辑封装成委托来传递;
        /// 
        /// 延伸一下:
        /// Linq To Sql:就可以把打开数据库连接,查询数据(不变的),把Sql的拼装(可变的逻辑);
        /// Linq To Xml:把数据解析这类动作封装起来(不变的逻辑),筛选条件封装成委托传递;
        /// Linq To Redis:  把固定逻辑封装起来,可变的逻辑封装成委托传递;
        /// Linq To Cache:...........
        /// Linq To JSON:...
        /// Linq To EveryThing:......
        /// 
        /// 现在大家觉得Linq是什么?  Linq是一种封装思想:
        ///  
        /// IEnumerable类型数据:可以理解成内存数据---来自于数据库的数据;把固定逻辑封装起来,不变的逻辑封装成委托传递;这是一种伟大的封装思想:对于用户来说,就不用关心内部怎么实现,只需要Linq一下即可;微软之前很推崇这种思想,只让开发者关注自己的核心逻辑,彻底的把开发者变成小白;  如果大家有经验,你们会发现像AspNetMVC就属于是傻瓜式开发;成套的;不用去关心原理,就可以直接上手;
        /// 
        ///但是现在又变了。。。现在有更高的要求,像LinqToJSON,Linq To Redis。。。。并没有人来封装这些; 如果大家有兴趣,可以去尝试去封装一下;老师看同学们作业完成情况;如果完成的,我就同意评讲;如果相对较少,我就不讲了;
        /// 
        ///IQueryable类型数据:可以是内存数据,数据库的数据;
        /// 
        /// Linq的常用操作:
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resource"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static List<T> RichardWhere<T>(this List<T> resource, Func<T, bool> func) where T:class
        {
    
    
            List<T> list = new List<T>();
            foreach (var item in resource)
            {
    
    
                //if (item.Name.Length > 2) //条件的判断其实就是逻辑---动作 //唯一的区别就在与中间这里的条件不一样;
                if (func.Invoke(item))
                {
    
    
                    list.Add(item);
                }
            }
            return list;
        }

        public static IEnumerable<T> RichardWhereEnumerable<T>(this IEnumerable<T> resource, Func<T, bool> func) where T : class
        {
    
    
            List<T> list = new List<T>();
            foreach (var item in resource)
            {
    
    
                //if (item.Name.Length > 2) //条件的判断其实就是逻辑---动作 //唯一的区别就在与中间这里的条件不一样;
                if (func.Invoke(item))
                {
    
    
                    list.Add(item);
                }
            }
            return list;
        }

        /// <summary>
        /// yield要和IEnumerable<T>配合使用,可以做到按需获取; 
        /// 找到一个就使用一个;
        /// 否则继续往后找;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resource"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static IEnumerable<T> RichardWhereIterator<T>(this IEnumerable<T> resource, Func<T, bool> func) where T : class
        {
    
    
            foreach (var item in resource)
            {
    
    
                Console.WriteLine("yield开始判断。。。");
                if (func.Invoke(item))
                {
    
    
                    yield return item; 
                }
            }
        }

        /// <summary>
        /// 如果换个条件怎么办?
        /// 
        /// 使用委托:委托可以把方法当做参数传递;方法其实是逻辑,委托可以把逻辑当做参数传递;
        /// 
        /// 委托:应该是返回值为bool的委托,参数是一个Student
        /// </summary>
        /// <param name="resource"></param>
        /// <returns></returns>
        public static List<Student> RichardWhere(this List<Student> resource, Func<Student, bool> func)
        {
    
    
            List<Student> list = new List<Student>();
            foreach (var item in resource)
            {
    
    
                //if (item.Name.Length > 2) //条件的判断其实就是逻辑---动作 //唯一的区别就在与中间这里的条件不一样;
                if (func.Invoke(item))
                {
    
    
                    list.Add(item);
                }
            }
            return list;
        }

    }

猜你喜欢

转载自blog.csdn.net/hello_mr_anan/article/details/108559538