委托和事件
- 委托
- 声明
- 实例化
- 调用
- 将类型安全的函数指针(方法)作为其他方法的参数进行传递,从而实现函数回调方法
- 委托:匿名方法委托
- 多播委托
- 委托:委托的异步调用
- 委托:委托的兼容性
- 事件
- 事件实际上是委托的一种特殊形式,C#使用一种委托模型来实现事件
- 事件的声明、订阅和取消
- 事件:实现的步骤
委托
-
委托是C#用来处理需用函数指针来处理的情况的
-
委托是完全面向对象的(同时封装了对象实例和方法),是类型安全的
-
委托可保存对方法的引用的类,但只能对于其签名匹配的方法进行引用
-
委托声明定义一个从 System.Delegate 类派生的类
-
委托实例封装了一个调用列表,该列表列出了一个或多个方法,每个方法称为一个可调用实体
-
对于实例方法,可调用实体由该方法和一个相关联的实例组成
声明
[委托修饰符] delegate 返回值类型 委托名 ([形参列表]);
实例化
委托名 委托实例名 = new 委托名(匹配方法);
调用
委托实例名(实参列表);
将类型安全的函数指针(方法)作为其他方法的参数进行传递,从而实现函数回调方法
delegate void D(int x); // 声明委托
class C
{
public static void M1(int i){Console.WriteLine("C.M1:" + i);}
public static void M2(int i){Console.WriteLine("C.M2:" + i);}
public void M3(int i){Console.WriteLine("C.M3:" + i);}
}
class Test
{
static void Main()
{
D d1 = new D(C.M1); //使用new关键字,创建委托对象,指向类静态方法
d1(-1); //调用M1
D d2 = C.M2; //使用赋值运算符,创建委托对象,指向类静态方法
d2(-2); //调用M2
C objc = new C();
D d3 = new D(objc.M3); //使用new关键字,创建委托对象,指向对象实例方法
d2(-3); //调用M3
Console.ReadKey();
}
}
委托:匿名方法委托
- 无需先声明类或结构以及与委托匹配的方法,而是在创建委托的实例时,直接声明与委托匹配的方法的代码块(匿名方法)
delegate void Printer(string s); //声明委托
class TestClass
{
static void Main()
{ // 使用匿名方法实例化delegate类
Printer p = delegate(string j)
{ Console.WriteLine(j); };
p("使用匿名方法的委托的调用。");
//匿名delegate调用结果
Console.ReadKey();
}
}
多播委托
委托也可以包含多个方法,这种委托称为多播委托
- 调用多播委托实例,则按顺序依次调用多播委托实例封装的调用列表中的多个方法
- 声明多播委托时,其返回类型必须为void
- 三个静态方法:Combine, Remove, RemoveAll
- 多播委托通过 + 或 += 向多播委托实例封装的调用列表中添加方法;通过 – 或 – = 从多播委托实例封装的调用列表中删除方法
delegate void D(int x);
class C
{
public static void M1(int i) { Console.WriteLine("C.M1: " + i);}
public static void M2(int i) { Console.WriteLine("C.M2: " + i);}
}
class Test
{
static void Main()
{ D cd1 = new D(C.M1); cd1(-1); //调用 M1
D cd2 = new D(C.M2); cd2(-2); //调用M2
D cd3 = cd1 + cd2; cd3(10); //先调用M1,然后调用M2
cd3 -= cd1; cd3(20); //删除M1,调用M2
cd3 -= cd2; //删除M2后,调用列表为null
// cd3(70); //抛出System.NullReferenceException异常
cd3 -= cd1; //没有M1可删除,但不报错
Console.ReadKey();
}
}
委托:委托的异步调用
委托:委托的兼容性
-
与委托向对应的方法不必与委托签名完全匹配
-
方法M与委托类型D兼容条件:
-
D和M的参数数目相同,且各自对应参数具有相同的ref或out修饰符
-
对于每个ref或out参数,D中的参数类型与M中的参数类型相同
-
存在从M的返回类型到D的返回类型的标识或隐式引用转换。即允许方法具有的派生返回类型比委托中定义的更多(协变)
-
每一个值参数(没有 ref 或 out 修饰符的参数)都存在从D中的参数类型到M中的对应参数类型的标识或隐式引用转换。允许方法具有的派生参数类型比委托类型中的更少(逆变)
事件
处理机制;事件的声明、订阅和取消;.NET Framework事件模型
-
一种使对象或类能够提供通知的成员
-
类或对象可以通过事件向其他类或对象通知发生的相关事情
-
发送(或引发)事件的类称为“发行者”(生产者),接收(或处理)事件的类称为“订户”(消费者)
-
事件是对象发送的消息,以发信号通知操作的发生
-
在 .NET Framework 类库中,事件是基于 EventHandler 委托和 EventArgs 基类的
事件实际上是委托的一种特殊形式,C#使用一种委托模型来实现事件
-
事件模型分为事件生产者和事件消费者,其处理机制大致可以分为下列4步:
-
在事件生产者类中声明一个事件成员,即某种事件处理委托(简称为事件委托)的实例(多播事件委托实例);
-
在事件消费者类中声明与事件委托相匹配的事件处理方法;
-
通过“+=”向多播事件委托实例封装的调用列表中添加事件处理方法,或通过“-=”从多播事件委托实例封装的调用列表中删除事件处理方法;
-
在事件生产者类中添加有关发生事件的代码,即当满足某种条件时(发生事件),则调用委托,即调用多播事件委托实例封装的调用列表中添加的事件处理方法。如果没有订阅,即事件实例为Null,则不作任何处理
button.Click += new EventHandler(this.Button_Click);//按钮单击事件
private void Button_Click(object sender, EventArgs e) // 声明按钮单击事件处理方法
{
box.BackColor = System.Drawing.Color.Green; // 文本框背景色改为绿色
}
事件的声明、订阅和取消
-
声明
[修饰符] event 事件委托名 事件名;
事件委托名(sender, e) -
订阅和取消
对象.事件名 +=委托实例;
对象.事件名 -=委托实例;
//声明事件处理委托
public delegate void SampleEventHandler(object sender, EventArgs e);
public class Publisher
{
public event SampleEventHandler SampleEvent; //声明事件
protected virtual void RaiseSampleEvent() //产生事件
{ //若不需要传递消息参数,可直接使用EventArgs对象
SampleEvent(this, new EventArgs()); //引发事件
}
}
public class Subscriber
{
public static void Method1(object sender, EventArgs e)
{
Console.WriteLine("To Do Something...");
}
public static void Method2(object sender, EventArgs e)
{
Console.WriteLine("To Do Something...");
}
}
public static void Main()
{ / /创建委托实例
SampleEventHandler d1 = new SampleEventHandler(Subscriber.Method1);
Publisher p = new Publisher();
//订阅事件
p.SampleEvent += d1;
p.SampleEvent += new SampleEventHandler(Subscriber.Method2);
//订阅事件
p.SampleEvent -= d1; //取消事件
Console.ReadKey();
}
事件:实现的步骤
-
声明提供事件数据的类,从System.EventArgs派生
-
声明事件处理委托
-
声明引发事件的类
-
在事件生产类中,声明事件
-
在事件生产类中,实现产生事件的代码
-
声明处理事件的类
-
在事件消费类中,声明事件处理方法
-
在事件消费类中,订阅或取消事件