第一阶段C#-14:委托,事件
1_委托
(1)委托,又称代理,是一种存储函数引用的类型,就像定义string str一样,这个str变量就是string类型,C#中没有函数类型,但可以定义一个委托,把函数赋予这个委托
(2)当一个函数的返回值类型和参数列表(参数的个数,参数的顺序,类型)和某个委托匹配时,这个委托就可以代理这个方法(这个函数就可以赋值给委托)
(3)C#是一个强类型的编程语言,强类型的编程语言的特性就是所有的东西都有特定的类型,变量,字段,属性...在定义的时候都需要显示的指定他们的类型,方法(函数)之前没有数据类型,委托就是一个数据类型,委托类型的变量可以存放函数
(4)当方法接受了一个委托类型参数,相当于接受类一个方法,接受的这个方法必须满足委托定义的参数和返回值
(5)委托声明在类中或者类外(命名空间下),委托的声明就是声明了一个方法的的类型,相当于多个数据类型,这个数据类型的变量,可以保存函数
(6)委托格式:访问修饰符 delegate 返回值类型 委托的名字(参数列表)
public delegate void Mydelegate1();//声明了一个没有返回值没有参数的委托,只能匹配没有返回值没有参数的函数
public delegate void Mydelegate2(string s);
public delegate int Mydelegate3(string s);
main()
{Test.TestFn(MyFn);
}
static void MyFn(){}
class Test
{//这个方法接受一个委托类型的参数,相当于接受了一个方法,这个接受的方法必须满足这个委托的定义的参数和返回值public static void TestFn(Mydelegate1 f)()
}
2_委托的实例化
(1)委托没有无参构造,实例化委托的时候必须传递参数(方法),参数类型和委托保持一致
(2)委托指向了方法的地址,调用委托相当于间接调用了方法
(3)委托的实例化可以简写:委托类型 委托变量名 =方法名;
(4)委托的另一种调用:委托变量名.Invoke(参数);
delegate void MyDel(int v1,string v2);class Test{public Test(){MyDel fn1 =new MyDel(Fn1);Fn1(1, "111");//直接调用fn1(1, "111");//间接调用 MyDel n1 = Fn2;//委托的实例化可以简写//委托的另外一种调用方法Console.WriteLine(n1.Invoke(10, "10"));}void Fn1(int a,string b){Console.WriteLine("张三");}int Fn2(int a,int b){return 0;}}
3_多播委托
(1)包含多个方法的委托称之为多播委托
(2)使用+=运算符,在委托变量上再次添加一个方法,使用-=运算符从委托中移除一个方法
(3)如果一个委托变量存储了多个方法,当委托被调用的时候,将会执行所有的方法并传递参数
(4)但一个委托操作两次new Test().Fn时候,操作的不是一个方法,是两个实例的Fn;可以将对象使用变量保存,也可以将方法声明成静态的
delegate void MyDel(string v);
static void Main(string[] args){MyDel fn1 = new MyDel(Fn1);//包含多个方法的委托称之为多播委托//使用+=运算符 在委托变量上再次添加一个方法fn1 += new MyDel(new Test().Fn2);//如果这个委托类型的变量存储了多个方法,当委托被调用的时候,将会执行所有的方法并传递参数fn1("张三");//Fn1和Fn2都执行MyDel fn2 = Fn1;fn2 += new Test().Fn2;fn2("李四");//可以使用-=从委托中移除一个方法fn2 -= Fn1;fn2("王武");
//--------------------------------------Console.WriteLine("===================");MyDel fn3 = Fn1;fn3 += new Test().Fn2;fn3-= new Test().Fn2;//这个委托同样会被调用两次,因为上面Fn2操作的不是一方法,是两个实例的Fn2fn3("张三");
//相当于MyDel fn4 = Fn1;Test t1= new Test();fn4 += t1.Fn2;Test t2 = new Test();fn4 -= t2.Fn2;fn4("李四");//解决办法//1、将对象使用变量保存,然后执行+=和-=惭怍MyDel fn5 = Fn1;Test t3 = new Test();fn5 += t3.Fn2;fn5-= t3.Fn2;fn5("李四");
//2、将方法声明成静态的MyDel fn6 = Fn1;fn6 += Test.Fn3;fn6 -= Test.Fn3;fn6("李四");
}public static void Fn1(string v){Console.WriteLine("这是Fn1中的v==="+v);
}class Test{public void Fn2(string v){Console.WriteLine("这是Fn2中的v===" + v);}public static void Fn3(string v){Console.WriteLine("这是Fn3中的v===" + v);}}
4_委托的调用
(1)委托被调用的时候会执行该委托中会执行该委托变量中存储的所有的方法,当委托没有存储任何方法时,执行就会报错,未将对象引用设置到对象的实例委托的默认值时null
class Test {public delegate int Mydel(int x);public Mydel fns { get; set; }
public Test(){fns += Fn1;fns += Fn2;
}public int Fn1(int v){Console.WriteLine(v * 100);return v * 100;}public int Fn2(int v){Console.WriteLine(v * v);return v * v;}public void Run(){//委托在被调用的时候会执行该委托变量中存储的所有的方法//当委托中没有存储任何方法的时候,执行就会报错,未将对象引用设置到对象的实例//委托的默认值是null/* if (fns != null){fns(10);}*/
//简写:使用?.运算符,当前面的值为空的时候,后续代码不会执行//?. 用于可能为null的值上面获取其他的属性,即使为null,也不会报错fns?.Invoke(10);}}
(2)?. 运算符,当前面的值为空的时候,后续代码就不会执行,用于可能为null的值上获取其他的属性,即使为null,也不会报错
People people= new People();
Console.WriteLine(people.Name);
People p2 = null;
Console.WriteLine(p2?.Name);
class People{public string Name { get; set; }}
5_内置委托
(1)内置委托有 Action,Func;没有返回值的时候使用Action<参数1的类型,参数2的类型,......>;有返回值的时候使用Func<参数1的类型,参数2的类型,......,返回值类型> (2)Action是一种数据类型,表示没有返回值没有参数的函数的数据类型;
Action<int,string>表示参数1为int,参数2为string并且没有返回值的函数的数据类型
(3)Func<>也是一种数据类型,表示有返回值的函数的数据类型;
Func<int,>表示无参,返回值为int类型的函数
Func<int,string,bool>表示参数1为int,参数2为string,返回中为bool的函数
6_泛型委托
(1)委托可以使用泛型,泛型委托调用的时候需要定义类型
class MYArray
{//4.0利用内置的委托,能查询多种类型的数组,泛型委托调用的时候需要定义类型public static T MyFind4<T>(T[] arr, Func<T,bool> f){for (int i = 0; i < arr.Length; i++){if (f(arr[i])){return arr[i];}}//返回当前类型的默认值return default(T);}
}
7_事件
7.1
(1)事件的定义:使用event关键字,EventHandler 委托类型 ;MyClick就是委托的实例,事件变量
public static event EventHandler MyClick; //默认值 null
(2)事件:C#中,事件就是委托的实例(个例)委托的实例不一定是事件,但是,事件一定是委托的实例;事件就是特殊的委托,安全的委托,受到CLR管理
(3)事件的定义位置:类的内部,方法的外部
(4)事件的实例化(事件的绑定)和委托实例化基本一致,只是第一次初始化的时候,也可以使用-=或+=
(5)调用事件:在控制台的应用程序中,咱们人为“立即”调用,在Winform中,一个按钮绑定事件(给事件变量赋值),事件的执行有滞后性,比如:点击鼠标移入移出窗体加载