相关文章推荐
慷慨大方的毛巾  ·  QT教程— 1.4 ...·  9 月前    · 
俊逸的青蛙  ·  如何在 Azure Functions ...·  1 年前    · 
不敢表白的芒果  ·  MySQL - GROUP BY ...·  1 年前    · 
  • Lambda的表达式:()=>{}
  • Lambda的本质:一个匿名函数,在底层,会在一个尖括号(<>)类中,生成带有名称的方法。
  • Linq的本质(代码的封装思想):从技术上说:是通过扩展方法来完成的,从程序设计上说:把不变的业务逻辑转移,固定的业务逻辑封装,整合起来就是Linq。
  • 2.Lambda的前世今生

    .NetFramework2.0 匿名方法 增加了一个delegate关键字,可以访问到除了参数以外的局部变量 .NetFramework3.0 去掉delegate关键了,在参数的后增加了一个=> goes to .NetFramework3.0后期,去掉了匿名方法红的参数类型,编译器提供了语法糖,可以推导出类型的参数

    namespace MyLambdaLinq
        public delegate void NoReturnNoParaOutClass();
        public delegate void GenericDelegate<T>();
        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时代
    				int j = 0;
    				NoReturnNoPara method = new NoReturnNoPara(DoNothing);
    				NoReturnWithPara method1 = new NoReturnWithPara(Study);
    				method1.Invoke(123, "HelloWord");
                //.NetFramework2.0  匿名方法  增加了一个delegate关键字,可以访问到除了参数以外的局部变量
                int i = 0;
    				NoReturnWithPara method = new NoReturnWithPara(delegate (int x, string y)
    					Console.WriteLine(x);
    					Console.WriteLine(y);
    					Console.WriteLine(i);
                //.NetFramework3.0  去掉delegate关键了,在参数的后增加了一个=>  goes to
    				NoReturnWithPara method = new NoReturnWithPara((int x, string y) =>
    					Console.WriteLine(x);
    					Console.WriteLine(y);
    					Console.WriteLine(i);
    				//.NetFramework3.0 后期,去掉了匿名方法红的参数类型--为什么可以去掉?  语法糖:编译器提供的便捷功能;可以推导出类型的参数
    				NoReturnWithPara method = new NoReturnWithPara((x, y) =>
    					Console.WriteLine(x);
    					Console.WriteLine(y);
    					Console.WriteLine(i);
    				//如果匿名方法体中只有一行代码,可以省略方法体的大括号; 
    				NoReturnWithPara method = new NoReturnWithPara((x, y) => Console.WriteLine(x));
    				NoReturnWithPara method1 = (x, y) => Console.WriteLine(x);
            private void DoNothing()
                //如果要在这里调用变量j
                Console.WriteLine("This is DoNothing");
            private void Study(int id, string name)
                Console.WriteLine($"Id:{id},Nam: {name}");
    

    二、匿名类

    Object:因为C#是强类型语言,Object需要在编译时才能确定类型。
    --定义的字段无法被调用

    dynamic:可以避开编译器的检查。
    --定义的字段可以被调用
    --调用没有定义的字段在运行前也不会出异常
    --可给字段重新赋值。

    var:弱类型,编译时确定类型。
    --定义的字段可以被调用
    --调用的字段没有定义的话会出现异常
    --无法给字段重新赋值
    --无法声明方法
    --无法赋值为null,必须进行初始化赋值,且赋的值必须让能推算出类型。
    --不允许作为方法的参数的类型使用
    (建议var使用场景:需要使用到匿名类的时候,在不确定具体什么类型的时候,复杂类型的时候。缺点是:代码可读性变差,开发中建议尽量明确类型)

    三、扩展方法

    扩展:扩充,让现有的功能更强大,增加原本不存在的功能
    扩展方法:在不修改原有类的情况下,增加一个新的方法,并且可以像实例方法一样调用。

    扩展方法需要满足的条件:
    1.必须是静态类中的静态方法
    2.扩展的是谁,谁就要作为第一个参数,且这个参数前面需要增加this关键字

    应用场景:
    1.扩展第三方类库,第三方类库多是引用dll方式引入,无法直接修改代码,可以使用扩展方法给第三方类库增加新功能。
    2.祖传项目维护,本着老代码能不动就不动的原则(老代码一般补丁代码过多,可能修改一个地方就会导致其他地方出现问题),使用扩展方法进行新功能增加。

    1.普通的扩展方法

    现有的一个学生类:

    /// <summary>
    /// 学生类
    /// </summary>
    public class Student
    	/// <summary>
    	/// 年龄
    	/// </summary>
    	public int Age { get; set; }
    	/// <summary>
    	/// 名字
    	/// </summary>
    	public string Name { get; set; }
    	/// <summary>
    	/// 学习方法
    	/// </summary>
    	public void Study()
    		Console.WriteLine($"{Age}岁的{Name}在这里是学习");
    

    定义一个扩展类:

    此处的StudentExtension为静态方法,StudyMore方法的参数前增加了this关键字

    /// <summary>
    /// 扩展方法
    /// </summary>
    public static class StudentExtension
    	public static void StudyMore( this Student student)
    		Console.WriteLine($"{student.Age}岁的{student.Name}这里是学习更多");
    
    static void Main(string[] args)
    	Student student = new Student();
    	student.Name = "张三";
    	student.Age = 23;
    	//Student类中原有的学习方法
    	student.Study();
    	//扩展类中新增的扩展方法
    	student.StudyMore();
    

    执行结果:

    2.类型的扩展方法(建议使用时指定具体类型进行扩展)

    1.普通类型都可以进行方法扩展,示例中的IntToString()方法
    2.泛型可以进行方法扩展。但是具有侵入性(表示所有的类型都会拥有这个方法,覆盖面太广),可能会导致一些类型存在了一些不应该存在的行为,如果使用错误便会导致bug发生,如示例3。
    3.Object是所有类型的父类,所有也会出现泛型的问题。
    4.如果版本升级导致dll等引用中,增加了扩展方法,同时也在类的内部增加了一个同样的方法,在调用的时候,会优先调用类内部声明的方法;

    演示用的扩展类:

    public static class ExtensionMethod
    	/// <summary>
    	/// 示例1:int的扩展方法
    	/// </summary>
    	/// <param name="i"></param>
    	/// <returns></returns>
    	public static string IntExtension(this int i)
    		return i.ToString();
    	/// <summary>
    	/// 示例2:泛型的扩展方法
    	/// </summary>
    	/// <typeparam name="T"></typeparam>
    	/// <param name="t"></param>
    	/// <returns></returns>
    	public static string GenericsExtension<T>(this T t)
    		return t.ToString();
    	/// <summary>
    	/// 示例3:泛型的扩展方法
    	/// </summary>
    	/// <typeparam name="T"></typeparam>
    	/// <param name="t"></param>
    	/// <returns></returns>
    	public static int GenericsExtension2<T>(this T t)
    		return Convert.ToInt32(t);
    	/// <summary>
    	/// 示例4:Object的扩展方法
    	/// </summary>
    	/// <param name="o"></param>
    	/// <returns></returns>
    	public static string ObjectExtension(this Object o)
    		return o.ToString();
    

    调用演示:

    static void Main(string[] args)
    	int i = 0;
    	//示例1:int的扩展方法
    	i.IntExtension();
    	//示例2:泛型的扩展方法
    	i.GenericsExtension<int>();
    	//示例3:泛型的扩展方法的弊端演示
    	string s = "传入字符换类型之后这个方法将会报错";
    	s.GenericsExtension2<string>();
    	//示例4:Object的扩展方法的弊端演示
    	i.ObjectExtension();
    

    3.实战演示

    如果字符串长度超过5,就只取前五个字符

    public static class ExtensionMethod
    	/// <summary>
    	/// 如果字符串长度超过5,就只取前五个字符
    	/// </summary>
    	/// <param name="str"></param>
    	/// <returns></returns>
    	public static string ToSubString(this string str, int maxLength = 5)
    		//如果字符串为空,返回空
    		if (string.IsNullOrEmpty(str))
    			return string.Empty;
    		//如果字符串长度大于5截取前五位,否则返回原字符串
    		if (str.Length > maxLength)
    			return str.Substring(0, 5);
    			return str;
    

    调用演示:

    static void Main(string[] args)
    	// 如果字符串长度超过5,就只取前五个字符
    	string str1 = "这是六个字哦";
    	Console.WriteLine($"字符串大于五个字的时候:{str1.ToSubString()}");
    	string str2 = "这是五个字";
    	Console.WriteLine($"字符串等于五个字的时候:{str2.ToSubString()}");
    	string str3 = "这字";
    	Console.WriteLine($"字符串小于五个字的时候:{str3.ToSubString()}");
    	string str4 = "";
    	Console.WriteLine($"字符串为空的时候:{str4.ToSubString()}");
    

    执行结果:

    四、Linq的原理及示例

    Linq to Object:通过方法封装+不变的逻辑封装在方法中,可变的逻辑通过委托传递+扩展方法
    Linq to sql:把不变的逻辑封装在内,可变的sql语句通过委托传递
    Linq to xml:把不变的处理xml的逻辑封装在内,可变的处理xml的逻辑通过委托传递

    1.使用[扩展方法+委托]模拟Linq的原理

    与linq中where的区别: linq的底层是通过迭代器实现循环的

    技术手段来说:通过拓展方法来完成的

    扩展方法:

    public static class ExtensionMethod
    	#region Test1
    	//public static bool isOk(Student student)
    	//	return student.age < 18;
    	//static Func<Student, bool> Func = isOk;
    	#endregion
    	/// <summary>
    	/// 按条件筛选学生信息
    	/// </summary>
    	/// <param name="studentList"></param>
    	/// <param name="func">Func<Student,bool> func → 相当于上方Test1的代码</param>
    	/// <returns></returns>
    	public static List<Student> FilteringStudentData(this List<Student> studentList, Func<Student, bool> func)
    		List<Student> stuList = new List<Student>();
    		foreach (var stu in studentList)
    			if (func.Invoke(stu))
    				stuList.Add(stu);
    		return stuList;
    	/// <summary>
    	/// 按条件筛选信息 泛型+委托+扩展方法
    	/// </summary>
    	/// <param name="studentList"></param>
    	/// <param name="func"></param>
    	/// <returns></returns>
    	public static List<T> FilteringData<T>(this List<T> old_List, Func<T, bool> func)
    		List<T> list = new List<T>();
    		foreach (var item in old_List)
    			if (func.Invoke(item))
    				list.Add(item);
    		return list;
    
    static void Main(string[] args)
    	List<Student> list = new List<Student>()
    		new Student()
    			Name = "张三",
    			Age = 18
    		new Student()
    			Name = "李四",
    			Age = 17
    		new Student()
    			Name = "王二",
    			Age = 19
    	//第一种调用方式
    	Func<Student, bool> func = new Func<Student, bool>(s =>
    		return s.Age < 18;
    	var student = list.FilteringStudentData(func);
    	//第二种调用方式(第一种的简化)
    	var stu = list.FilteringStudentData(s => s.Age < 18);
    	//等同于Linq中的.Where()
    	var stu_linq = list.Where(s => s.Age < 18);
    

    2.改造方法更接近Linq(IEnumerable+yieId)

    扩展IEnumerable,使用yieId(状态机的实现)关键字(yieId必须和IEnumerable配套使用)
    ----IEnumerable通常认为是一个内存数据,类似的还有IQueryable
    ----yieId关键字做到了按需获取,判断时,只要符合条件就返回,如果不返回就继续往后判断

    扩展方法:

    public static class ExtensionMethod
    	/// <summary>
    	/// IEnumerable+YieId
    	/// </summary>
    	/// <param name="studentList"></param>
    	/// <param name="func"></param>
    	/// <returns></returns>
    	public static IEnumerable<T> Where_YieId<T>(this IEnumerable<T> old_List, Func<T, bool> func)
    		foreach (var item in old_List)
    			if (func.Invoke(item))
    				yield return item;
    
    static void Main(string[] args)
    	List<Student> list = new List<Student>()
    		new Student()
    			Name = "张三",
    			Age = 18
    		new Student()
    			Name = "李四",
    			Age = 17
    		new Student()
    			Name = "王二",
    			Age = 19
    	IEnumerable<Student> stuList = list.Where_YieId(s => s.Age < 18);
    	foreach (var item in stuList)
    		Console.WriteLine($"名字:{item.Name},年龄:{item.Age}");
    

    五、Linq常见的语句

    除以下常用示例外,关于Linq的文档:Linq相关文档(阿里云盘)

    static void Main(string[] args)
    	List<Student> list = new List<Student>()
    		new Student()
    			Name = "张三",
    			Age = 18,
    			Class = "一班"
    		new Student()
    			Name = "李四",
    			Age = 17,
    			Class = "二班"
    		new Student()
    			Name = "王二",
    			Age = 19,
    			Class = "一班"
    	#region Linq的两种方式
    			var stuList1 = list.Where
    				x => x.Age > 18
    				).ToList();
    			foreach (var item in stuList1)
    				Console.WriteLine($"stuList1结果展示:Name:{item.Name},Age:{item.Age}");
    			var stuList2 = from s in list
    						   where s.Age > 18
    						   select s;   //list里面必然是符合要求的数据;
    			foreach (var item in stuList2)
    				Console.WriteLine($"stuList2结果展示:Name:{item.Name},Age:{item.Age}");
    			#endregion
    	#region Linq to Object
    	#region 投影
    			var list1 = list
    				.Where(
    				x => x.Age > 18//条件过滤
    				.Select(s => new//投影:可以自由组装需要的信息.----通过new一个匿名类或者具体类,定义对象之后进行赋值使用
    					people = s.Name + "-" + s.Age,
    					describe = s.Age > 18 ? "成年人" : "未成年人"
    			foreach (var item in list1)
    				Console.WriteLine($"list1结果展示:people:{item.people},describe:{item.describe}");
    			var list2 = from s in list
    						where s.Age > 18 //条件过滤
    						select new //投影:可以自由组装需要的信息.----通过new一个匿名类或者具体类,定义对象之后进行赋值使用
    							people = s.Name + "-" + s.Age,
    							describe = s.Age > 18 ? "成年人" : "未成年人"
    			foreach (var item in list2)
    				Console.WriteLine($"list2结果展示:people:{item.people},describe:{item.describe}");
    			#endregion
    	#region 过滤、排序
    			var list3 = list
    				.Where(
    				x => x.Age > 18 //条件过滤
    				.Select(s => new//投影:可以自由组装需要的信息.----通过new一个匿名类或者具体类,定义对象之后进行赋值使用
    					people = s.Name + "-" + s.Age,
    					describe = s.Age > 18 ? "成年人" : "未成年人",
    					Age = s.Age
    				 .OrderBy(s => s.Age)//排序 升序
    				.ThenBy(s => s.describe) //多重排序,可以多个字段排序都生效
    				.OrderByDescending(s => s.Age)//倒排
    				.Skip(2)//跳过几条  //必须要先排序
    				.Take(3)//获取几条 //必须要先排序
    			foreach (var item in list3)
    				Console.WriteLine($"list3结果展示:people:{item.people},describe:{item.describe}");
    			#endregion
    	#region 分组
    			var list4 = from s in list
    						where s.Age > 18 //条件过滤
    						group s by s.Class into g
    						select new
    							Class = g.Key,
    							MaxAge = g.Max(s => s.Age)
    			foreach (var item in list4)
    				Console.WriteLine($"list4结果展示:Class:{item.Class},MaxAge:{item.MaxAge}");
    			var list5 = list.GroupBy(s => s.Class).Select(x => new
    				Class = x.Key,
    				MaxAge = x.Max(x => x.Age)
    			foreach (var item in list5)
    				Console.WriteLine($"list5结果展示:Class:{item.Class},MaxAge:{item.MaxAge}");
    			#endregion
    	#region join、left join连接查询
    			//join
    			var list6 = from s in list
    						join c in stuList1 on s.Class equals c.Class //只能使用equals不能使用==
    						select new
    							Name = s.Name,
    							Age = s.Age,
    							Class = s.Class
    			foreach (var item in list6)
    				Console.WriteLine($"list6结果展示:Name:{item.Name},Class:{item.Class}");
    			var list7 = list.Join(stuList1, s => s.Class, c => c.Class, (s, c) => new
    				Name = s.Name,
    				Age = s.Age,
    				Class = s.Class
    			foreach (var item in list7)
    				Console.WriteLine($"list7结果展示:Name:{item.Name},Class:{item.Class}");
    			//left join
    			var list8 = from s in list
    						join c in stuList1 on s.Class equals c.Class into g
    						from x in g.DefaultIfEmpty()
    						select new
    							Name = s.Name,
    							Age = s.Age,
    							Class = s.Class,
    							describe = x == null ? "没有该班级" : "有该班级"
    			foreach (var item in list8)
    				Console.WriteLine($"list8结果展示:Name:{item.Name},Class:{item.Class}");
    			var list9 = list.Join(stuList1, s => s.Class, c => c.Class, (s, c) => new
    				Name = s.Name,
    				Age = s.Age,
    				Class = s.Class,
    				describe = c == null ? "没有该班级" : "有该班级"
    			}).DefaultIfEmpty();
    			foreach (var item in list9)
    				Console.WriteLine($"list9结果展示:Name:{item.Name},Class:{item.Class}");
    			#endregion
    	#endregion
    复制代码
    分类:
    后端
    标签: