C#之委托和事件入门
今天是新年开始上班的第一天,祝大家工作顺利。接下来我们一起来学习C#中的委托和事件。它们两个的关系非常密切,我们先从委托开始。
1 委托的定义、声明与调用
创建一个名为MyDelegate的控制台项目,并添加一个DelegateClass.cs。
public delegate void NoReturnWithPara(int x, int y);//声明委托
声明委托和声明方法非常类似,只是在返回类型前添加一个delegate关键字。
该委托所对应的方法是带两个参数且无返回值的,所以在实例化时要注意方法的签名要与委托的声明一致。
//定义一个带两个参数无返回值的方法
private static void Plus(int x, int y)
{
Console.WriteLine(x + " " + y);
}
NoReturnWithPara methodPlus = Plus; //实例化委托
需要说明的是,在实例化委托时,只需将方法名称赋值给委托即可,无需添加小括号和参数。
//另一种实例化委托的方法
NoReturnWithPara methodPlus = new NoReturnWithPara(Plus);
调用委托和调用一个方法是一样的
methodPlus(3, 4); //调用委托
**2 利用委托解除耦合**
再添加一个GreetingClass.cs用来实现用不同的语言来打印问候语。
如果用普通方法,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyDelegate
{
//代表不同国家类型的枚举变量
public enum PeopleType
{
Chinese,
English
}
public class GreetingClass
{
public static void SayHelloChinese(string name) //声明委托
{
Console.WriteLine("你好 " + name);
}
public static void SayHelloEnglish(string name)
{
Console.WriteLine("hello " + name);
}
public static void SayHello(string name, PeopleType type)
{
if (type == PeopleType.Chinese)
SayHelloChinese(name);
else
SayHelloEnglish(name);
}
}
}
然后再Program.cs中调用相关方法
GreetingClass.SayHello("张三", PeopleType.Chinese);
GreetingClass.SayHello("Bob", PeopleType.English);
这样做虽然可以实现我们的功能,但如果要进行扩展就要修改SayHelloChinese()和SayHelloEnglis()两个方法,可以用委托来实现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//利用委托解除耦合
namespace MyDelegate
{
public delegate void GreetingHandle(string name);//声明委托
public class GreetingClass
{
public static void SayHelloChinese(string name)
{
Console.WriteLine("你好 " + name);
}
public static void SayHelloEnglish(string name)
{
Console.WriteLine("hello " + name);
}
//定义其中一个参数为委托类型(指代一个特定的方法,即把一个方法作为参数)
//单一职责
public static void SayHello(string name, GreetingHandle handle)
{
handle(name); //调用这个特定的方法
}
}
}
//实例化两个不同的委托 分别代表两个不同的方法
GreetingHandle chineseHandle = GreetingClass.SayHelloChinese;
GreetingHandle englishHandle = GreetingClass.SayHelloEnglish;
GreetingClass.SayHello("张三", chineseHandle); //将委托作为方法的参数
GreetingClass.SayHello("Bob", englishHandle);
3 委托的多播
当实例化了一个委托后,这个委托变量中就包含了一个方法,还可通过+=
来向该委托变量中添加多个方法,当调用该委托变量时会按添加的顺序全部执行这些方法,当然这些方法的签名必须与委托的签名一致。
NoReturnWithPara methodPlus = Plus; //实例化委托
NoReturnWithPara methodPlus = new NoReturnWithPara(Plus);
//委托的多播
methodPlus += Plus;
methodPlus += Plus; methodPlus += Plus;
methodPlus += Plus;
//遍历该委托中的所有方法
foreach(var item in methodPlus.GetInvocationList())
{
Console.WriteLine(item); //打印方法的名称
}
methodPlus(3, 4); //调用委托中的所有方法
4 利用委托多播实现观察者模式
在该项目中添加多个类,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyDelegate
{
//委托是一种类型 事件是委托类型的实例(某种类型的变量)
public class Cat
{
public void Miao()
{
Console.WriteLine("猫叫");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyDelegate
{
public class Dog
{
public static void DogCall()
{
Console.WriteLine("狗叫");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyDelegate
{
public class People
{
public static void Awake()
{
Console.WriteLine("人醒来");
}
}
}
要实现的功能是,当调用Cat类的Miao()时依次调用Dog类的DogCall()和People类的Awake()方法。若采用常规方法,可直接在Miao()方法中直接调用其它两个方法。
public void Miao()
{
Console.WriteLine("猫叫");
Dog.DogCall();
People.Awake();
}
然后再Program中调用Miao()方法即可
Cat cat = new Cat();
cat.Miao();
这样实现的弊端就是,如果在调用Miao()方法后,不仅要调用刚才所说的两个方法,还要调用其它方法,就要修改Cat类的Miao()方法,这样不利于类的封装,我们就可以通过委托来实现在调用Miao()方法时来确定后续要调用的方法,这样就更加灵活,即观察者模式。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyDelegate
{
public delegate void CatHandler(); //声明一个委托
public class Cat
{
public CatHandler CatHandlerMethod; //实例化一个委托
public void Miao()
{
Console.WriteLine("猫叫");
//Dog.DogCall();
//People.Awake();
if (CatHandlerMethod != null)
CatHandlerMethod(); //执行委托
}
}
}
上述代码中的委托类型变量CatHandlerMethod所包含的方法是不确定的,在外部调用Miao()方法时,可动态地往该类的委托类型的成员变量CatHandlerMethod中添加方法。
Cat cat = new Cat();
//观察者模式
cat.CatHandlerMethod += Dog.DogCall;
cat.CatHandlerMethod += People.Awake; //为委托添加方法
cat.Miao();
5 事件与委托间的关系
委托是一种类型,而 事件是委托类型的实例(某种类型的变量),就是用委托来声明一个变量,这个变量就是事件。事件与委托的功能与用法类似,之所以要用委托来定义一个事件,就是为了安全。
接下来用事件来实现刚才的观察者模式
public class Cat
{
public event CatHandler CatHandlerEvent; //声明一个是事件
public void Miao()
{
Console.WriteLine("猫叫");
//Dog.DogCall();
//People.Awake();
if (CatHandlerEvent != null)
CatHandlerEvent();
}
}
声明事件只是比实例化委托时多了一个event
关键字,接下就是在调用Miao()方法前为事件添加相应的方法
Cat cat = new Cat();
//为事件添加方法 事件不能用new来初始化,只能用+=来添加方法 发防止被赋值为null
//且事件不能在外部调用
cat.CatHandlerEvent += Dog.DogCall;
cat.CatHandlerEvent += People.Awake;
cat.Miao();
事件为了实现安全,它在委托的基础上又两个限制,第一个是不能用new
关键字来实例化一事件,用来防止一个事件被赋值为null。第二个是事件不能在外部被调用,即CatHandlerEvent 只能在Cat类中的被调用,不能在Program.cs中执行CatHandlerEvent ();语句,这样也保障了事件的安全性。
所以委托和事件之间的区别与联系就是:委托是类型,事件是委托的实例。