一…委托:
此处简单介绍委托,后面会再开专题详细介绍
(一)委托的意义
:委托实际上是一个类型,用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的使用:
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没有的时候,使用默认值