1:委托的概念定义?
其实委托和类是一样的,是一种用户自定义类型。但是类表示的是数据和方法的集合,而委托则是持有一个或者多个方法,以及一系列定义的操作
2:那怎么使用委托类型呢?
(1):声明一个委托类型。委托实际上看上去方法特别的像,唯一的一个区别就是委托没有方法体。
(2):使用委托的时候要先声明一个变量。
(3):创建委托类型的对象,把他赋值给委托变量。新的委托对象括号里面包括某个方法的引用,这个方法和(第一步定义的签名和返回类型一致)。
(4):你可以选择为委托对象增加其他方法。(这些方法必须与第一步中定义的委托类型有相同的签名和返回类型)。
(5):在代码中你可以像调用方法一样调用 委托。在调用方法时候,其包含的每一个方法都会被执行。
下面是一个案例
deleate void MyDey(int value);//声明一个委托类型
class Program{
//定义两个方法
void PrintLow(int value){
Console.WriteLine("{0}-LOW Value",value);
}
void PrintHight(int value){
Console.WriteLine("{0}—Hight Value",value);
}
static void Main(){
Program program=new Program();
MyDey del;//声明委托变量
//生成自己一个随机数的方法,得到0到99之间的一个随机数
Random rand=new Random();
int randomValue=rand.Next(99);
//创建包含PrintLow和PrintHight委托对象并且将得到的随机数赋值给del
del=randomValue>50?new MyDel(program.PrintLow):new MyDey(Program.printHght)
//执行委托
del(randomValue);
}
}
*委托保存的方法可以来自任何类和结构,只要满足下面两点匹配
1:委托的返回值类型一样 2:委托的签名也必须样即使包含ref和out修饰符
*调用列表中的方法可以是实例方法也可以是静态方法
*在调用委托会执行调用列表中所有方法
3:声明委托类型(引用类型)
委托是类型,就像类一样,类在最开始时候要定义,委托也一样委托类型必须在被用来创建变量以及里欸行的对象之前声明。
delegate void MyDel(inty x);
以delegate关键字开头
没有方法体
4:创建委托对象
因为委托是类型所以是引用类型,因此有引用和对象。创建委托对象方式有两种如下:
:new运算符 委托类型名。
:一组圆括号,括号里面就是第一个成员的方法的名字,方法可以是实例方法也可以是静态方法。
delVar=new MyDel(myIntObj.MyM1);//创建并且保存引用里面的方法是实例方法
delVar=new MyDel(Sclass.OtherM2);//创建并且保存引用里面的方法是静态方法
第二种方为快捷语法,如下代码所示
delVar=myIntObj.MyM1;//创建并且保存引用里面的方法是实例方法
delVar=Sclass.OtherM2;//创建并且保存引用里面的方法是静态方法
5:组合委托
我们讲到现在为止,委托在调用列表中都只有一个方法。委托可以使用额外的运算符来组合两个委托,这个运算符最终创建一个新的委托。例如:
delA=myIntObj.MyM1;//创建并且保存引用里面的方法是实例方法
delB=Sclass.OtherM2;//创建并且保存引用里面的方法是静态方法
delC=delA+delB;//组合调用列表
*尽管组合委托让我们感觉委托被改变了,但是事实上委托是恒定的不变的,上面又重新生成一个新的委托,没有在以前的基础上改变。
6:给委托中添加方法和删除方法
我们上面说委托是不变的,但是我们可以给委托中添加方法和删除方法。
例如:添加方法
MyDel delVar=inst.MyM1;//创建并初始化
delVar+=Scl.m3;//添加方法
delVar+=X.Act//添加方法
*其实委托是不可变的,所以委托的调用列表添加3个方法后的结果其实是变量指向的一个全新的委托。
例如:移除方法
MyDel delVar=inst.MyM1;//创建并初始化
delVar—=Scl.m3;//添加方法
delVar—=X.Act//添加方法
*与委托添加方法一样,其实是创建了一个新的委托。新的委托是旧委托的副本知识没有了已经被移除方法的引用
如下移除时候需要记住的一些事项
如果在调用列表中的方法有多个实例,-=运算符将从列表最后开始搜索,并且移除第一个与方法匹配的实例
试图删除委托中没有的方法是没有效果的
试图调用空委托会抛出异常。我们通过weituohenull进行比较来判断委托的调用列表是由为空。
7:调用委托
就像调用方法一样简单的调用委托,用于调用委托的参数将会用于调用列表中的每一个方法
MyDel delVar=inst.MyM1;//创建并初始化
delVar—=Scl.m3;//添加方法
delVar—=X.Act//添加方法
delVar(55)//参数为55
nst.MyM1(55)
Scl.m3(55)
X.Act(55)//所以每一个方法都会调用一次
8:委托的例子
8.1这是一个没有参数和返回值的委托
Test类中定义两个打印函数
Main方法在创建了委托的实例并且增加了另外三个方法
程序随后调用了委托,也就调用了他的方法。在调用委托之前,程序将进行委托之前判断他是否为null
//定义了一个没有返回值和参数的委托类型
delegate void PrintFunction();
class Test{
public void Print1(){
Console.WriteLine("实例方法一")
}
public static void Print2(){
Console.WriteLine("静态方法一")
}
}
class program{
static void Main(){
Test t=new Test();//创建一个测试欸的实例
PrintFunction pf//创建一个空的委托
pf=t.Print1;//实例化并且初始话给委托,并且将实例方法的引用交给给委托
//委托增加三个方法
pf+=Test.Print2;
pf+=t.Print1;
pf+=Test.Print2;
//线下委托有4个方法
if(null!=pf){
pf();//调用委托
}else{
Console.Writeline("出现错误了");
}
}
}
//代码输出的结果为
实例方法一
静态方法一
实例方法一
静态方法一
8.2调用带有返回值的委托
delegate int MyDel();//声明没有返回值的方法
class Myclass{
int IntValue=5;
public int Add2(){IntValue+=2;return IntValue}
public int Add3(){IntValue+=3;return IntValue}
}
class Program{
static void Main(){
MyClass mc=new MyClass();
MyDel mDel=mc.Add2;
mDel+=mc.Add3;
mDel+=mc.Add2;
Console.WriteLine("Value{0}",mDel());//调用委托并使用返回值
}
}
//结果为12
8.3调用带有引用参数的委托
*在调用委托列表中的下一个方法时候,参数的新值(不是初始值)会传给下一个方法
delegate void MyDel(ref int x);//声明没有返回值的方法
class Myclass{
public int Add2(ref int x){x+=2;}
public int Add3(ref int x){x+=3;}
static void Main(){
MyClass mc=new MyClass();
MyDel mDel=mc.Add2;
mDel+=mc.Add3;
mDel+=mc.Add2;
int x=5;
mDel(ref x)
Console.WriteLine("Value{0}",x);
}
}
//输出的值为12
9:匿名方法
匿名方法:是在初始话委托时内联声明发方法
那么为什么要有匿名方法呢?我们见过了使用实例方法和静态方法,有时时候委托只是用来调用一次初始化,我们没必要独立创建一个类来单独的存这个方法,这时候匿名方法就派上用场了
例如:
//命名方法
class Program{
public static int Add20(int x){return x+=20}
delegate int OtherDel(int InParam)
static void Main(){
OtherDel del=Add20;
Console.WriteLine("{0}",del{5});
Console.WriteLine("{0}",del{6});
}
}
//匿名方法
class Program{
delegate int OtherDel(int InParam);
static void Main(){
OtherDel delegate(int x){return+=20};
Console.WriteLine("{0}",del{5});
Console.WriteLine("{0}",del{6});
}
}
//输出结果为
25 26
9.1什么时候又还会使用匿名方法呢?
声明委托变量时作为初始化表达式
组合委托时在赋值语句的右边
为委托增加事件时在赋值语句的也右边(事件会在下篇文章中介绍)
9.2匿名参数的语法
delegate类型的关键字
参数列表,如果语句块没有使用任何参数则可以省略
语句块,包含了匿名方法的代码
delegate(参数列表) {语句块}
9.2.1返回类型
如果委托有void类型的返回值,匿名方法就不能返回值。
如果委托类型是int。匿名方法 的实现代码因此必须在代码路径中返回int
class Program{
delegate int OtherDel(int InParam);
static void Main(){
OtherDel delegate(int x){return+=20};
}
}
9.2.2参数
除了数组参数,匿名方法的的参数列表必须在如下3个方面与委托匹配
参数数量
参数类型及位置
修饰符
我们可以通过圆括号为空或者圆括号来简化匿名方法的参数列表,但是必须满足以下两个条件
委托的参数列表不包括任何out参数
匿名参数不使用任何参数
9.2.3Params参数
如果委托的参数列表出现了Params关键字,那么在匿名参数就可以省略params参数
例如:
delegate void SomeDel(int x params int[] y );
SomeDel mDel=delegate(int x,int[] y){};//匿名方法里面的params关键字就可以省略
9.2.4变量和参数的作用域
参数以及声明在匿名方法内部的局部变量的作用域限制在实现方法的主体之内。/
外部变量
与委托的具名方法不同,匿名方法可以访问它们外围作用域的局部变量和环境。
外围作用域的变量叫做外部变量
用来匿名方法实现代码中的外部变量称为被方法捕获
10:Lambda表达式
为社么要有Lambda表达式?
就在上面我们已经学过匿名方法,但是在匿名方法中还是要写delegate关键字,因为编译器已近知道我们在将方法赋值委托,所以我们可以使用Lambda表达式
例如:
MyDel del=delgate(int x){return x+1}//匿名方法
MyDel lel=(int x)=>{return x+1}//Lambda表达式
还可以更加的简单,编译器还可以从委托的声明中知道委托参数类型,所以Lambda表达式就准许我们可以省略类型的参数
*带有类型的参数列表称为显式类型
*省略类型的参数列表称为隐式参数
如果只有一个隐式类型参数,我们可以省略周围的圆括号 如le3的赋值代码所示
最后,Lambda表达式的主体是语句块或表达式。。如果语句块包含了一个返回语句,我们可以将语句块替换为return关键字后的表达式如le4的赋值代码所示
MyDel del=delegate(int x){retuen x+1;};//匿名方法
MyDel del= (int x)>={retuen x+1;};//Lambda表达式
MyDel del= (x)>={retuen x+1;};//Lambda表达式
MyDel del= x>={retuen x+1;};//Lambda表达式
MyDel del= x >=x+1;//Lambda表达式
*有关Lambda表达式的列表的要点如下?
Lambda表达式参数列表中的参数必须在参数数量,类型和位置上与委托相匹配
表达式的参数列表中的参数不一定需要包含类型(隐式类型),除非委托有ref或者out参数(此时必须注明类型)
如果只有一个参数,并且式隐式类型,周围的圆括号可以被省略,否则必须有括号。
没有参数,必须使用一组空的圆括号。