C#从委托、lambda表达式到linq总结
前言
本文总结学习C#必须知道的基础知识,委托、监视者模式、常用lambda表达式、linq查询,自定义扩展方法,他们之间有什么关系呢?匿名委托是如何演变成lambda表达式,lambda再如何导出linq语句的?
委托
用delegate关键字声明委托,引用MSDN上的一段内容:委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的调用可以像其他任何方法一样,具有参数和返回值。
using System;
namespace ConsoleApplication1
{
//这个委托的名字是MyDel,委托的类型是MyDel
//这个委托代表了一系列函数,这一些函数必须是没有返回值,没有参数的
public delegate void MyDel();
//这个委托代表了一系列函数,这一系列函数必须是没有返回值,参数为string类型
public delegate void MyDel2(string name);
//这个委托代表了一系列函数,这一系列函数必须是int类型的返回值,两个int类型的参数
public delegate int MyDel3(int a,int b);
class Program
{
static void Main(string[] args)
{
//创建委托的2种方法
//1.如果使用new关键字创建委托,则必须使用一个函数初始化这个委托对象
MyDel2 my1 = new MyDel2(print);
//2.如果不用new关键字,则可以直接赋值
MyDel2 my2 = print2;
//3.委托和他封装的方法具有相同的功能
my1("ss");
my2("ww");
//4.既然委托代表了一系列函数,那么一个委托对象可以承接多个函数
Console.WriteLine("委托对象承接多个函数");
my1 += print2;
my1("aa");
//5.在承接的函数集中删减函数
Console.WriteLine("委托对象删减函数");
my1 -= print2;
my1("bb");
Console.ReadKey();
}
public static void print(string name) {
Console.WriteLine("print-----------"+name);
}
public static void print2(string n) {
Console.WriteLine("22222-----------"+n);
}
}
}
监视者模式
监视者模式是在微软平台大量存在的一种模式,通俗一点它就是事件,事件就是监视者模式。比如生活中,你睡觉的时候委托同学上课叫醒你,这就是一个监视者模式,你是被监视者,是委托方,同学是被委托方,监视者。下面一个例子是:考试的时候自己委托父母监视自己,考的好的话让父母奖励,考差了则受罚的监视者模式:
using System;
namespace ConsoleApplication1
{
public delegate void Del();
class Program
{
static void Main(string[] args)
{
//创建对象
Student s = new Student();
Parent p = new Parent();
//s.p = () => { Console.WriteLine("超常发挥,耶");};
//s.k = () => { Console.WriteLine("考差了,嘤嘤嘤"); };
//学生对象的委托承接着家长对象的方法
s.p += p.PPrice;
s.k += p.KKit;
//s开始执行考试
s.Exam(70);
Console.ReadKey();
}
}
public class Student {
public Del p = null;
public Del k = null;
//执行考试
public void Exam(int f) {
if (f < 60)
{
k();
}
else {
p();
}
}
}
public class Parent {
public void PPrice() {
Console.WriteLine("考的不错,奖励个馒头");
}
public void KKit() {
Console.WriteLine("考的太差,面壁思过");
}
}
}
匿名委托
匿名委托也是一种委托,只不过没有方法名,可以理解为用delegate代替了匿名委托的方法名,很多情况不必要创建方法,需要临时创建方法来调用时使用,下面例子很好的说明匿名委托的不同用法
using System;
namespace ConsoleApplication1
{
public delegate void Delsing();
public delegate int DelPlus(int a,int b);
public delegate string DelString(string a,int b);
class Program
{
static void Main(string[] args)
{
//委托承接命名函数直接执行
Delsing d = Sing;
d();
//匿名委托直接执行
Delsing d1 = delegate() { Console.WriteLine("匿名委托"); };
d1();
//带参数且有返回值的匿名委托,执行后用write方法显示
DelPlus d2 = delegate(int j, int k) { return j + k; };
Console.WriteLine(d2(1,2));
//带参数且有返回值的匿名委托,当做参数来传,然后调用函数实现功能;用定义的其他方法执行委托
DelString d3=delegate(string a,int b){return a+b;};
Test(d3,"1+1=",2);
Console.ReadKey();
}
public static void Test(DelString del, string a, int b) {
string str = del(a,b);
Console.WriteLine(str);
}
public static void Sing() {
Console.WriteLine("I'm singing");
}
}
}
lambda表达式
lambda表达式其实就是匿名委托精简之后的形式,在参数和方法体中补上“=>”(称为goes to)来表示lambda表达式,下面是lambda表达式不同形式的总结
//先定义一个Del开头的委托
public delegate void Del1();
//匿名委托
//转成lambda表达式时要去掉delegate 加上=>
Del1 d1=delegate(){Console.WriteLine("ss");};
d1=()=>{Console.WriteLine("ss");};
//由于没有参数,那么()不能省略
public delegate int Del2();
Del2 d2=delegate(){return 1;};
d2=()=>{return 1;};
//如果是直接返回,换句话说就是没有业务逻辑处理,就是只有一条返回语句,可以把{}换成()同时去掉return关键字
d2=()=>(1);
//如果方法体中有业务逻辑,则必须使用{}
d2=()=>{if(2>1){return 1;}else{return 2;}};
//
public delegate void Del3(string a);
Del3 d3=delegate(string a){Console.WriteLine(a);};
d3=(string a)=>{Console.WriteLine(a);};
//可以把参数的类型去掉,因为系统会自动判断参数的类型,毕竟是把这个lambda赋值给了对应的委托
d3=(a)=>{Console.WriteLine(a);};
//若只有一个参数,则不需要()
d3=a=>{Console.WriteLine(a);};
//
public delegate int Del4(int a,int a);
Del4 d4=delegate(int a,int b){return a+b;};
d4=(int a,int b)=>{return a+b;};
d4=(a,b)=>{return a+b;};
d4=(a,b)=>(a+b);
d4=(a,b)=>a+b;
d5=a=>a+1;
Linq语言集成化查询
linq不同于结构化查询语言(SQL)它不仅可以查询数据而且可以查询对象。
LINQ的官方中文名称为“.NET语言集成查询”,英文全称为“Language-Integrated Query”。它提供了类似于SQL语法的遍历,筛选与投影功能,LINQ不仅能完成对对象的查询,它可以透过DLINQ操作数据库,或是透过XLINQ控制XML。我们来比较一下两个不同方法求出数组中大于20的数据的例子:
using System;
using System.Collections.Generic;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
int[] a = { 2,23,25,32,5,64,52,30};
//求出数组中大于20的数据
List<int> list = new List<int>();
foreach (int b in a) {
if (b > 20) {
list.Add(b);
}
}
foreach (int c in list) {
Console.WriteLine(c);
}
Console.ReadKey();
}
}
}
2.用IEnumerable的Where扩展方法,通过lambda表达式,精简程序执行过程,需要导入命名空间:using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
int[] a = { 2,23,25,32,5,64,52,30};
//求出数组中大于20的数据
//lambda表达式
//var list = a.Where(p => { return p > 20; });
//简化后 var可以接受任何类型
var list = a.Where(p => p > 20);
//Where 返回类型时IEnumerable
//IEnumerable<int> list = a.Where(p=>p>20);
foreach (int c in list) {
Console.WriteLine(c);
}
Console.ReadKey();
}
}
}
上面可以算是小的LINQ查询,但能算是真正的LINQ,我暂时理解为广义的LINQ。例子中(p=>p>20)其实是一个lambda表达式,是微软定义好的一个委托,从这个委托的特点我们知道它有一个参数,返回值是bool类型。数组肯定实现了IEnumerable接口,而Where是IEnumerable<(Of <T>)>
成员的一个扩展方法,MSDN中定义为Where(Func<(Of<(UMP,Boolean)>)>)
基于谓词筛选值序列。LINQ查询调用了微软定义好的扩展方法进行查询,下面我们插入一段扩展方法的内容,帮助理解上面LINQ的Where扩展方法。
扩展方法
通俗点说就是在不更改原来类的基础上,为类添加方法。需要注意的是:1.扩展方法必须写在静态类中;2.扩展方法必须是静态方法,虽然是静态方法,但这个扩展方法是为对象扩展的,只能由对象调用。它的定义是:
public static class 类名 {
public static 返回值 方法名(this 要扩展的类型 对象名[,参数列表]) {
}
}
我们来做个扩展方法,为String类型扩展个获取文件类型的方法
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
public class Program
{
public static void Main()
{
//调用扩展方法获取文件类型
string file = @"E:\FTPPUBLISH\学习资料\KindEditor\kindeditor-v4.0.3\examples\colorpicker.html";
Console.WriteLine(file.GetFileType());
string sss = "18.9.06.mp3";
Console.WriteLine(sss.GetFileType());
Console.ReadKey();
}
}
public static class ExtendMethod
{
public static string GetFileType(this string str)
{
string[] strs = str.Split('.');
return strs[strs.Length - 1];
}
}
}
理解了这些扩展方法,相信会有助于你理解上面LINQ查询时所用的Where扩展方法。下面我们继续来看LINQ查询,把上面的LINQ再做一步扩展:
用LINQ语句进行查询,并将结果降序分组排序的三种方式
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
public class Program
{
public static void Main()
{
List<Student> list = new List<Student>();
list.Add(new Student(){Age=10,Name="Jack",Address="bj"});
list.Add(new Student(){Age=67,Name="Mack",Address="郑州"});
list.Add(new Student(){Age=23,Name="Dack",Address="USA"});
list.Add(new Student(){Age=56,Name="Cack",Address="bj"});
list.Add(new Student(){Age=8,Name="Eack",Address="郑州"});
list.Add(new Student(){Age=34,Name="Hack",Address="bj"});
list.Add(new Student(){Age=18,Name="小红",Address="USA"});
Console.WriteLine("查询出集合中年龄大于45的学生");
//查询出集合中年龄大于45的学生(完整形式,一般不这样写)
//Func<Student, bool> f = p => p.Age > 45;
//IEnumerable<Student> result = list.Where<Student>(f);
//简写
var result0 = list.Where<Student>(p=>p.Age>45);
foreach (Student s in result0) {
Console.WriteLine(s.Age+" "+s.Name);
}
Console.WriteLine("查询集合中年龄小于30,并按年龄降序排列,按城市分组");
//查询集合中年龄小于30,并按年龄降序排列,按城市分组
Console.WriteLine("____________________第一种方法____________________");
IEnumerable<Student> result1 = list.Where(p => p.Age < 30).OrderByDescending(p => p.Age);
IEnumerable<IGrouping<string, Student>> result11 = result1.GroupBy<Student, string>(p => p.Address);
foreach (IGrouping<string, Student> gg in result11)
{
foreach (Student s in gg)
{
Console.WriteLine(s.Age + ";" + s.Name + ";" + s.Address);
}
}
Console.WriteLine("____________________第二种方法____________________");
var result2 = list.Where(p => p.Age < 30).OrderByDescending(p => p.Age).GroupBy(p=>p.Address);
//第一次GetEnumerator()得到IEnumerator<IGrouping<string, Student>>
var c = result2.GetEnumerator();
while (c.MoveNext()) {
//第二次GetEnumerator()得到IEnumerator<Student>
var d = c.Current.GetEnumerator();
while (d.MoveNext()) {
//.Current获取集合中位于枚举数当前位置的元素
Console.WriteLine(d.Current.Name+";"+d.Current.Address);
}
}
Console.WriteLine("____________________第三种方法____________________");
var result3 = from p in list
where p.Age < 30
orderby p.Age descending
group p by p.Address;
foreach (var ss in result3) {
foreach (var s in ss) {
Console.WriteLine(s.Age + ";" + s.Name + ";" + s.Address);
}
}
Console.ReadKey();
}
}
public class Student {
public int Age { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
}
最后我们做个小小的LINQ总结:LINQ语言集成化查询基础是泛型和lambda表达式,它的形式是:
from 元素 in 集合
Where 元素条件
orderby 元素.属性 ascending/descending
group 元素 by 元素.属性
select 元素
和SQL查询类似,上面例子中表明如果使用了groupby语句,则不需要select。
参考资料
本文参考下文,并对文中的例子稍微做了修改
从委托、lambda表达式到linq的一些个人小总结