C#中的Lambda语句与Linq(总)

一…委托:

	此处简单介绍委托,后面会再开专题详细介绍

(一)委托的意义

:委托实际上是一个类型,用delegate修饰,编译器编译时会翻译成一个继承自特殊父类delegate类的子类,声明一个委托,就是定义了一个自定义类,继承自Delegate父类,这个类是用来传递方法的,声明的标签就是可以传递方法的标签。

(二):委托的语法:

1.声明委托:

public delegate void NoReturnNoPara()*//自定义一个委托类,委托类的名字是 NoReturnNoPara,
//这个委托可以接受的方法是:无参数无返回值的任意方法。*
	public delegate void NoReturnWithPara(int x, string y);*//这个委托可以接受的方法是有两
//个参数,一个是int类型,一个是string类型,但是无返回值的方法。*
    public delegate int WithReturnNoPara();
    public delegate string WithReturnWithPara(out int x, ref int y);
    //2.委托实例化:委托实例化的语法格式是有一个演变过程的,从最初的跟类实例化语法相同的形式,
//到后面演变为Lambda。
   //先定义好两个方法备用
   
 	 private void DoNothing()
     {
           Console.WriteLine("This is DoNothing");
       }
    private void Study(int id, string name)
    {
        Console.WriteLine($"{id} {name} 学习.Net高级班");
    }

(1) 在 //.NetFramework1.0 1.1时代,实例化一个委托就像普通类的实例化一样,实例化一个委托,委托的构造函数就是传递一个方法。方法是一个已经定义好的方法。

NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);
 
  NoReturnWithPara method = new NoReturnWithPara(this.Study);//实例化委托,实例化对象名是method 
   method.Invoke(123, "董小姐");//调用方法,调用的时候传入方法需要的参数

(2) //.NetFramework2.0 时代,使用匿名方法了,

实例化委托的时候,不必要传递一个已经定义好的方法作为构造参数了,而是可以在实例化时,直接传递一个匿名方法,匿名方法是不需要声明方法名的,然而此时的匿名方法,需要前面加delegate关键字。匿名方法:

delegate(传递参数表)
{方法体};

//new NoReturnWithPara 表示实例化这个委托,delegate (int id, string name)表示这委托
//传递了一个匿名方法
NoReturnWithPara method = new NoReturnWithPara(delegate (int id, string name)
{
     Console.WriteLine(i);
     Console.WriteLine($"{id} {name} 学习.Net高级班");
 });
 method.Invoke(234, "Harley");

(3) .NetFramework3.0时代 把delegate关键字去掉,直接参数列表后面 增加了一个箭头(箭头读作:goes to),此时这种形式,就称作Lambda表达式。

// (int id, string name) => 表示定义了一个匿名方法
NoReturnWithPara method = new NoReturnWithPara(
 (int id, string name) =>
  {
      Console.WriteLine($"{id} {name} 学习.Net高级班");
  });
  method.Invoke(123, "Coder");

*也可以直接省略掉参数列表中的参数类型,因为可以根据委托推算出参数类型:

NoReturnWithPara method = new NoReturnWithPara(
 (id, name) =>
 {
     Console.WriteLine($"{id} {name} 学习.Net高级班");
 });//省略参数类型,编译器的语法糖,虽然没写,编译时还是有的,根据委托推算
method.Invoke(123, "加菲猫");

*如果匿名方法的方法体只有一行,则可以省略掉方法的大括号和分号,写为以下格式:

 NoReturnWithPara method = new NoReturnWithPara(
                    (id, name) => Console.WriteLine($"{id} {name} 学习.Net高级班"));
                //如果方法体只有一行,可以去掉大括号和分号
                method.Invoke(123, "ZJ");

*最后 实例化委托时候的new也可以省略:

NoReturnWithPara method = (id, name) => Console.WriteLine($"{id} {name} 学习.Net高级班");
                method.Invoke(123, "大涛"); //new NoReturnWithPara可以省掉,也是语法糖,编译器自动加上

总结:Lambda表达式是什么:它实际上是一个匿名方法的表示,在程序编译的时候,编译器会给它分配一个名字。

(三)系统自带委托

.net系统中为我们定义了一些自带的委托,编程时,如果尽量使用系统自带的委托,系统自带委托不能解决的,再自己自定义委托。系统自带委托包括以下几种:

1. 无返回值的自带委托:Action: 0到16个参数,无返回值的泛型委托

Action action0 = () => { };
Action action1 = s => Console.WriteLine(s); //s是传入参数,如果参数只有一个 可以省略小括号

*Action是一个无返回值的委托,它可以有参数列表,它的参数列表的个数可以从0个-16个。

2. 带返回值的自带委托:Func: 0到16个参数的带一个返回值的泛型委托

(1). //这个Func带有一个string类型的返回值,

Func<string> func = new Func<string>(
	    		delegate()
	    		{
	    			return DateTime.Now.ToShortDateString();
	    		}
		);

 

上面这个匿名方法定义,可以衍化为如下的Lambda表达式:

Func func = new Func(
()=>
{
     return DateTime.Now.ToShortDateString();
}
);

再继续简化:带返回值的匿名方法,如果方法体只有一行,且是一个带返回值的方法体,则可以省去大括号分号,还有return关键字。简化如下:

Func func = new Func(()=> DateTime.Now.ToShortDateString());

带返回值的委托进行方法调用:

string sResult = func.Invoke();

(2). Func:带一个返回值和一个参数的

Func<string,int> func2 = t=>int.Parse(t);//Func<string,int>,第一个是传入参数类型,
//第二个是返回值类型,表示func2是一个接受 传入参数是string类型,
//返回值为int类型方法的委托。

二.匿名类:

当一个方法要传出多个返回值的时候,会定义一个类来接收这些返回值,而在之前,需要先定义一个类,再在方法中实例化这个类的实例,传值给这个类的字段或者属性进行使用
如下:




public class Student
 		{
 			int Id;
 			string Name;
 			int Age;
 		}
public class UserStudent
{
		public  Student GetStudent()
		{
			........
			Student s = new Student();
			s.Id = 。。。;
			s.Name = 。。。;
			return s;
		}
		
}

在出现匿名类之后,就可以省去定义类的这一步,在需要使用类的地方直接定义匿名类:

object model = new 
 {
     Id = 2,
     Name = "undefined",
     Age = 25,
     ClassId = 2,
     Teacher = "Eleven"
 };

用object定义匿名类,在后面使用的时候编译环境是没办法在下面使用的时候,进行自动补全提示的。(点不出属性来)。
或者用var定义匿名类:var是一个语法糖,它可以根据右面的类型给你把前面变量的类型给推算出来。所以 用var来声明匿名类,在后面使用的时候,可以进行自动补全提示。

var model = new 
     {
         Id = 2,
         Name = "undefined",
         Age = 25,
         ClassId = 2,
         Teacher = "Eleven"
     };

定义匿名类之后,编译器在编译后会给生成一个类,并且系统会给这个匿名类定义一个名字。
*使用var的注意事项:
a. 用var声明变量,变量必须明确的初始化,必须能推断出类型的初始化值(值不可以是null)。
b.只能声明局部变量,不能是字段,也不能是静态属性。
c. 复杂形式的变量可以用var修饰。
另外:dynamic 关键字:dynamic是一个动态类型(var是一个语法糖),dynamic本质是一个字典,

dynamic dynamicValue = 123;//将值存入dynamicValue中
//dynamic类型后面可以引用任意属性,编译时都不会有错,但如果实际没有,则在运行时会报错
string sValue = dynamicValue.Id;

三 扩展方法:

(一) 扩展方法的声明:

1.必须在一个静态类中声明扩展方法;

2.扩展方法本身也必须是一个静态方法;

3.扩展方法的第一个参数的类型,是这个扩展方法要针对扩展的那个类,并且第一个参数前面要加this 例子如下:

//静态类
		public static class ExtendMethod 
{
		== 扩展方法本身也是静态的,第一个参数表示要给int?这种类型扩展一个 ToInt的方法,第一个参数前				   
		加this ==
		public static int ToInt(this int? iParameter) 
		{
				return  iParameter??-1;
		}
}

(二)扩展参数的使用:可以像普通静态方法一样使用,也可以直接用扩展方法针对的类型的实例化变量来调用:

int? iValue = 1;
//普通静态方法一样调用
ExtendMethod.ToInt(iValue);
//直接用实例化变量调用
iValue.ToInt();

(三) 如果扩展方法跟实例方法相同,优先调用实例方法;

(四) 扩展方法可以在不修改原来类封装的情况下,给类额外的扩展一个方法。

(五)扩展方法不要滥用:不要轻易在父类上增加扩展方法。

四.Linq To Object

Linq 主要是 集合类的一组扩展方法,主要用于集合的过滤

(一)在出现Linq前的常规数据过滤:

//找出一个List中年纪小于30的集合
			List<Student> studentList = this.GetStudentList();//得到一个Student的List
            var list = new List<Student>();
            foreach (var item in studentList)
            {
                if (item.Age < 30)
                {
                    list.Add(item);
                }
            }

(二) 使用Linq进行过滤:

var result = studentList.Where<Student>(s => s.Age < 30);

分析 :在系统类库中 Where的方法是这样定义的:

//该方法是定义在 Enumerable静态类中的;
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);

这个方法,第一个参数是针对要扩展的类的变量,前面加了this,第二个变量是一个系统自带委托Func,Func是一个有一个返回值,并且可以传入0至16个参数的系统自带委托。当前这个委托Func传入的是前面的集合中的类的类型,返回的是一个布尔表达表达式
具体的例子如下:

var result = studentList.Where<Student>(s => s.Age < 30);
 后面的委托可以写成:
 Func<Student,bool> predicate = s=>s.Age<30;经过简化的Lambda,第一个s是传入参数(编译器可以根据语法糖
 识别s是一个Student类,=>后面是函数体,return s.Age<30 (返回s.Age是否小于30),但是函数体只有return一行,因此省略return)
 
 var result = studentList.Where<Student>(predicate);

//调用时同样可以写成:

Enumerable.Where<Student>(studentList, s => s.Age < 30);

(三) Linq中的扩展方法原理:

都使用了迭代器的方法,思想就是设计模式中的迭代器模式:
迭代器的有点就是可以延迟加载,在用到的时候才去计算出真实的集合

(四)Linq中的 查询运算符 与 查询表达式

查询运算符方式:

var result = studentList.Where<Student>(predicate);

查询表达式方式:

var list = from s in studentList
                           where s.Age < 30
                           select s;

实质上,查询表达式跟查询运算符方式是一样的,在编译的时候,编译器会将后面查询表达式方式翻译成上面的查询运算符的方式。

(五)Linq的其他使用:

A. Linq除了使用where这种对集合进行过滤的功能,还可以使用投影:

Select(),Select投影可以建立一个匿名类对查询结果所需要的内容进行返回。
var result = studentList.Where<Student>(s => s.Age < 30)
									.Select(s=>new 
									{
										IdName = s.Id,
										ClassName = s.ClassId == 2?"高级班":"其他班"
									});
var list = from s in studentList
                           where s.Age < 30
                           select new
                           {
                           		IdName = s.Id,
								ClassName = s.ClassId == 2?"高级班":"其他班"
                           };

B.Join的使用:

Inner Join

studentList,classList两个集合连接在一起,主要连接主键的时候不能用==,只能用equals
查询表达式:

var list = from s in studentList
                           join c in classList on s.ClassId equals c.Id//不能用==只能equals
                           select new
                           {
                               Name = s.Name,
                               CalssName = c.ClassName
                           };

查询运算符:studentList,第一个集合,classList需要Join的第二个集合,s => s.ClassId第一个集合Join的主键,c => c.Id第二个集合需要Join的主键,第四个参数是一个投影的匿名类

var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
                {
                    Name = s.Name,
                    CalssName = c.ClassName
                });

左连接(Left Join)

表达式

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,为空则用
                           };

左连接加了一个 into scList,意思是 studentList Join classList 插入一个全新的集合 scList,表示studentList和classList 进行连接,如果classList 有值就使用,如果classList 没有值就给它一个默认值(null),在进行投影的时候进行定义,如果是null,则用 “无班级”。

运算符:

var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
                {
                    Name = s.Name,
                    CalssName = c.ClassName
                }).DefaultIfEmpty();

就是加一个默认值DefaultIfEmpty,在classList没有的时候,使用默认值

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值