• C#从委托、lambda表达式到linq总结


    前言

    本文总结学习C#必须知道的基础知识,委托、监视者模式、常用lambda表达式、linq查询,自定义扩展方法,他们之间有什么关系呢?匿名委托是如何演变成lambda表达式,lambda再如何导出linq语句的?

    委托

    用delegate关键字声明委托,引用MSDN上的一段内容:委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的调用可以像其他任何方法一样,具有参数和返回值。

    1. using System;
    2. namespace ConsoleApplication1
    3. {
    4. //这个委托的名字是MyDel,委托的类型是MyDel
    5. //这个委托代表了一系列函数,这一些函数必须是没有返回值,没有参数的
    6. public delegate void MyDel();
    7. //这个委托代表了一系列函数,这一系列函数必须是没有返回值,参数为string类型
    8. public delegate void MyDel2(string name);
    9. //这个委托代表了一系列函数,这一系列函数必须是int类型的返回值,两个int类型的参数
    10. public delegate int MyDel3(int a,int b);
    11. class Program
    12. {
    13. static void Main(string[] args)
    14. {
    15. //创建委托的2种方法
    16. //1.如果使用new关键字创建委托,则必须使用一个函数初始化这个委托对象
    17. MyDel2 my1 = new MyDel2(print);
    18. //2.如果不用new关键字,则可以直接赋值
    19. MyDel2 my2 = print2;
    20. //3.委托和他封装的方法具有相同的功能
    21. my1("ss");
    22. my2("ww");
    23. //4.既然委托代表了一系列函数,那么一个委托对象可以承接多个函数
    24. Console.WriteLine("委托对象承接多个函数");
    25. my1 += print2;
    26. my1("aa");
    27. //5.在承接的函数集中删减函数
    28. Console.WriteLine("委托对象删减函数");
    29. my1 -= print2;
    30. my1("bb");
    31. Console.ReadKey();
    32. }
    33. public static void print(string name) {
    34. Console.WriteLine("print-----------"+name);
    35. }
    36. public static void print2(string n) {
    37. Console.WriteLine("22222-----------"+n);
    38. }
    39. }
    40. }

    监视者模式

    监视者模式是在微软平台大量存在的一种模式,通俗一点它就是事件,事件就是监视者模式。比如生活中,你睡觉的时候委托同学上课叫醒你,这就是一个监视者模式,你是被监视者,是委托方,同学是被委托方,监视者。下面一个例子是:考试的时候自己委托父母监视自己,考的好的话让父母奖励,考差了则受罚的监视者模式:

    1. using System;
    2. namespace ConsoleApplication1
    3. {
    4. public delegate void Del();
    5. class Program
    6. {
    7. static void Main(string[] args)
    8. {
    9. //创建对象
    10. Student s = new Student();
    11. Parent p = new Parent();
    12. //s.p = () => { Console.WriteLine("超常发挥,耶");};
    13. //s.k = () => { Console.WriteLine("考差了,嘤嘤嘤"); };
    14. //学生对象的委托承接着家长对象的方法
    15. s.p += p.PPrice;
    16. s.k += p.KKit;
    17. //s开始执行考试
    18. s.Exam(70);
    19. Console.ReadKey();
    20. }
    21. }
    22. public class Student {
    23. public Del p = null;
    24. public Del k = null;
    25. //执行考试
    26. public void Exam(int f) {
    27. if (f < 60)
    28. {
    29. k();
    30. }
    31. else {
    32. p();
    33. }
    34. }
    35. }
    36. public class Parent {
    37. public void PPrice() {
    38. Console.WriteLine("考的不错,奖励个馒头");
    39. }
    40. public void KKit() {
    41. Console.WriteLine("考的太差,面壁思过");
    42. }
    43. }
    44. }

    匿名委托

    匿名委托也是一种委托,只不过没有方法名,可以理解为用delegate代替了匿名委托的方法名,很多情况不必要创建方法,需要临时创建方法来调用时使用,下面例子很好的说明匿名委托的不同用法

    1. using System;
    2. namespace ConsoleApplication1
    3. {
    4. public delegate void Delsing();
    5. public delegate int DelPlus(int a,int b);
    6. public delegate string DelString(string a,int b);
    7. class Program
    8. {
    9. static void Main(string[] args)
    10. {
    11. //委托承接命名函数直接执行
    12. Delsing d = Sing;
    13. d();
    14. //匿名委托直接执行
    15. Delsing d1 = delegate() { Console.WriteLine("匿名委托"); };
    16. d1();
    17. //带参数且有返回值的匿名委托,执行后用write方法显示
    18. DelPlus d2 = delegate(int j, int k) { return j + k; };
    19. Console.WriteLine(d2(1,2));
    20. //带参数且有返回值的匿名委托,当做参数来传,然后调用函数实现功能;用定义的其他方法执行委托
    21. DelString d3=delegate(string a,int b){return a+b;};
    22. Test(d3,"1+1=",2);
    23. Console.ReadKey();
    24. }
    25. public static void Test(DelString del, string a, int b) {
    26. string str = del(a,b);
    27. Console.WriteLine(str);
    28. }
    29. public static void Sing() {
    30. Console.WriteLine("I'm singing");
    31. }
    32. }
    33. }

    lambda表达式

    lambda表达式其实就是匿名委托精简之后的形式,在参数和方法体中补上“=>”(称为goes to)来表示lambda表达式,下面是lambda表达式不同形式的总结

    1. //先定义一个Del开头的委托
    2. public delegate void Del1();
    3. //匿名委托
    4. //转成lambda表达式时要去掉delegate 加上=>
    5. Del1 d1=delegate(){Console.WriteLine("ss");};
    6. d1=()=>{Console.WriteLine("ss");};
    7. //由于没有参数,那么()不能省略
    8. public delegate int Del2();
    9. Del2 d2=delegate(){return 1;};
    10. d2=()=>{return 1;};
    11. //如果是直接返回,换句话说就是没有业务逻辑处理,就是只有一条返回语句,可以把{}换成()同时去掉return关键字
    12. d2=()=>(1);
    13. //如果方法体中有业务逻辑,则必须使用{}
    14. d2=()=>{if(2>1){return 1;}else{return 2;}};
    15. //
    16. public delegate void Del3(string a);
    17. Del3 d3=delegate(string a){Console.WriteLine(a);};
    18. d3=(string a)=>{Console.WriteLine(a);};
    19. //可以把参数的类型去掉,因为系统会自动判断参数的类型,毕竟是把这个lambda赋值给了对应的委托
    20. d3=(a)=>{Console.WriteLine(a);};
    21. //若只有一个参数,则不需要()
    22. d3=a=>{Console.WriteLine(a);};
    23. //
    24. public delegate int Del4(int a,int a);
    25. Del4 d4=delegate(int a,int b){return a+b;};
    26. d4=(int a,int b)=>{return a+b;};
    27. d4=(a,b)=>{return a+b;};
    28. d4=(a,b)=>(a+b);
    29. d4=(a,b)=>a+b;
    30. d5=a=>a+1;

    Linq语言集成化查询

    linq不同于结构化查询语言(SQL)它不仅可以查询数据而且可以查询对象。
    LINQ的官方中文名称为“.NET语言集成查询”,英文全称为“Language-Integrated Query”。它提供了类似于SQL语法的遍历,筛选与投影功能,LINQ不仅能完成对对象的查询,它可以透过DLINQ操作数据库,或是透过XLINQ控制XML。我们来比较一下两个不同方法求出数组中大于20的数据的例子:

    1. using System;
    2. using System.Collections.Generic;
    3. namespace ConsoleApplication2
    4. {
    5. class Program
    6. {
    7. static void Main()
    8. {
    9. int[] a = { 2,23,25,32,5,64,52,30};
    10. //求出数组中大于20的数据
    11. List<int> list = new List<int>();
    12. foreach (int b in a) {
    13. if (b > 20) {
    14. list.Add(b);
    15. }
    16. }
    17. foreach (int c in list) {
    18. Console.WriteLine(c);
    19. }
    20. Console.ReadKey();
    21. }
    22. }
    23. }

    2.用IEnumerable的Where扩展方法,通过lambda表达式,精简程序执行过程,需要导入命名空间:using System.Linq;

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. namespace ConsoleApplication2
    5. {
    6. class Program
    7. {
    8. static void Main()
    9. {
    10. int[] a = { 2,23,25,32,5,64,52,30};
    11. //求出数组中大于20的数据
    12. //lambda表达式
    13. //var list = a.Where(p => { return p > 20; });
    14. //简化后 var可以接受任何类型
    15. var list = a.Where(p => p > 20);
    16. //Where 返回类型时IEnumerable
    17. //IEnumerable<int> list = a.Where(p=>p>20);
    18. foreach (int c in list) {
    19. Console.WriteLine(c);
    20. }
    21. Console.ReadKey();
    22. }
    23. }
    24. }

    上面可以算是小的LINQ查询,但能算是真正的LINQ,我暂时理解为广义的LINQ。例子中(p=>p>20)其实是一个lambda表达式,是微软定义好的一个委托,从这个委托的特点我们知道它有一个参数,返回值是bool类型。数组肯定实现了IEnumerable接口,而Where是IEnumerable<(Of <T>)>成员的一个扩展方法,MSDN中定义为Where(Func<(Of<(UMP,Boolean)>)>)基于谓词筛选值序列。LINQ查询调用了微软定义好的扩展方法进行查询,下面我们插入一段扩展方法的内容,帮助理解上面LINQ的Where扩展方法。

    扩展方法

    通俗点说就是在不更改原来类的基础上,为类添加方法。需要注意的是:1.扩展方法必须写在静态类中;2.扩展方法必须是静态方法,虽然是静态方法,但这个扩展方法是为对象扩展的,只能由对象调用。它的定义是:

    1. public static class 类名 {
    2. public static 返回值 方法名(this 要扩展的类型 对象名[,参数列表]) {
    3. }
    4. }

    我们来做个扩展方法,为String类型扩展个获取文件类型的方法

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. namespace ConsoleApplication2
    5. {
    6. public class Program
    7. {
    8. public static void Main()
    9. {
    10. //调用扩展方法获取文件类型
    11. string file = @"E:FTPPUBLISH学习资料KindEditorkindeditor-v4.0.3examplescolorpicker.html";
    12. Console.WriteLine(file.GetFileType());
    13. string sss = "18.9.06.mp3";
    14. Console.WriteLine(sss.GetFileType());
    15. Console.ReadKey();
    16. }
    17. }
    18. public static class ExtendMethod
    19. {
    20. public static string GetFileType(this string str)
    21. {
    22. string[] strs = str.Split('.');
    23. return strs[strs.Length - 1];
    24. }
    25. }
    26. }

    理解了这些扩展方法,相信会有助于你理解上面LINQ查询时所用的Where扩展方法。下面我们继续来看LINQ查询,把上面的LINQ再做一步扩展:
    用LINQ语句进行查询,并将结果降序分组排序的三种方式

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. namespace ConsoleApplication2
    5. {
    6. public class Program
    7. {
    8. public static void Main()
    9. {
    10. List<Student> list = new List<Student>();
    11. list.Add(new Student(){Age=10,Name="Jack",Address="bj"});
    12. list.Add(new Student(){Age=67,Name="Mack",Address="郑州"});
    13. list.Add(new Student(){Age=23,Name="Dack",Address="USA"});
    14. list.Add(new Student(){Age=56,Name="Cack",Address="bj"});
    15. list.Add(new Student(){Age=8,Name="Eack",Address="郑州"});
    16. list.Add(new Student(){Age=34,Name="Hack",Address="bj"});
    17. list.Add(new Student(){Age=18,Name="小红",Address="USA"});
    18. Console.WriteLine("查询出集合中年龄大于45的学生");
    19. //查询出集合中年龄大于45的学生(完整形式,一般不这样写)
    20. //Func<Student, bool> f = p => p.Age > 45;
    21. //IEnumerable<Student> result = list.Where<Student>(f);
    22. //简写
    23. var result0 = list.Where<Student>(p=>p.Age>45);
    24. foreach (Student s in result0) {
    25. Console.WriteLine(s.Age+" "+s.Name);
    26. }
    27. Console.WriteLine("查询集合中年龄小于30,并按年龄降序排列,按城市分组");
    28. //查询集合中年龄小于30,并按年龄降序排列,按城市分组
    29. Console.WriteLine("____________________第一种方法____________________");
    30. IEnumerable<Student> result1 = list.Where(p => p.Age < 30).OrderByDescending(p => p.Age);
    31. IEnumerable<IGrouping<string, Student>> result11 = result1.GroupBy<Student, string>(p => p.Address);
    32. foreach (IGrouping<string, Student> gg in result11)
    33. {
    34. foreach (Student s in gg)
    35. {
    36. Console.WriteLine(s.Age + ";" + s.Name + ";" + s.Address);
    37. }
    38. }
    39. Console.WriteLine("____________________第二种方法____________________");
    40. var result2 = list.Where(p => p.Age < 30).OrderByDescending(p => p.Age).GroupBy(p=>p.Address);
    41. //第一次GetEnumerator()得到IEnumerator<IGrouping<string, Student>>
    42. var c = result2.GetEnumerator();
    43. while (c.MoveNext()) {
    44. //第二次GetEnumerator()得到IEnumerator<Student>
    45. var d = c.Current.GetEnumerator();
    46. while (d.MoveNext()) {
    47. //.Current获取集合中位于枚举数当前位置的元素
    48. Console.WriteLine(d.Current.Name+";"+d.Current.Address);
    49. }
    50. }
    51. Console.WriteLine("____________________第三种方法____________________");
    52. var result3 = from p in list
    53. where p.Age < 30
    54. orderby p.Age descending
    55. group p by p.Address;
    56. foreach (var ss in result3) {
    57. foreach (var s in ss) {
    58. Console.WriteLine(s.Age + ";" + s.Name + ";" + s.Address);
    59. }
    60. }
    61. Console.ReadKey();
    62. }
    63. }
    64. public class Student {
    65. public int Age { get; set; }
    66. public string Name { get; set; }
    67. public string Address { get; set; }
    68. }
    69. }

    最后我们做个小小的LINQ总结:LINQ语言集成化查询基础是泛型和lambda表达式,它的形式是:

    1. from 元素 in 集合
    2. Where 元素条件
    3. orderby 元素.属性 ascending/descending
    4. group 元素 by 元素.属性
    5. select 元素

    和SQL查询类似,上面例子中表明如果使用了groupby语句,则不需要select。

    参考资料

    本文参考下文,并对文中的例子稍微做了修改
    从委托、lambda表达式到linq的一些个人小总结

  • 相关阅读:
    Linux Kernel Makefiles Kbuild en
    Android 源码结构分析
    Linux Charger IC 驱动移植总结
    Battery Charging Specification Revision 1.2 中文版本
    OpenCV 3.4.2 Windows系统下的环境搭建(附带opencv_contrib-3.4.2)
    OpenCV 经纬法将鱼眼图像展开
    shell 循环结构
    OpenCV之Mat类使用总结
    shell 条件结构之 if 语句使用总结
    OpenCV Error: Unspecified Error(The Function is not implemented)
  • 原文地址:https://www.cnblogs.com/iwsx/p/7017569.html
Copyright © 2020-2023  润新知