一、委托
1、什么什么使用委托
比如A类中有aa方法,B类中有bb方法。首先在A中创建一个B对象,去调用bb方法。然后现在有特殊需求,需要在B中调用A中的aa方法。这样出现了对象之间的相互调用,是不被允许的,这样的需求就需要“委托”来解决。
委托一般用来处理事件、多线程、对象之间的逆向调用(传值)。
这部分代码是:类A的AA方法,以及在类A中创建了B的对象,并调用BB方法。
public class ClassA
{
ClassB classB=new ClassB();
public void ATest()
{
classB.BB();
}
public void AA()
{
Console.WriteLine("这是A类中的aa方法");
}
}
这部分代码是:类B的BB方法,以及在类B中创建A对象,调用AA方法。
public class ClassB
{
ClassA classA = new ClassA();
public void BTest()
{
classA.AA();
}
public void BB()
{
Console.WriteLine("这是类B的bb方法");
}
}
在调用部分,分别调用对应的的方法 。会发现在类A中创建B的对象时就会出现报错。
static void Main(string[] args)
{
ClassA classA=new ClassA();
classA.ATest();
ClassB classB=new ClassB();
classB.BTest();
}
2、委托的概念
通俗的理解:委托的本质就是用来代表一个或多个方法的。委托其实规定的是方法的”原型“,根据原型,定义符合要求的方法,委托变量就可以去代表这些方法。也就意味着,以前是直接调用方法,现在改成使用”委托变量“来调用。
方法的原型:指的是规定了方法的返回值类型,方法参数类型,参数个数。
现在我们可以通过创建一个委托,让”委托“与AA方法关联。然后类B使用委托调用类A的AA方法。
使用委托来实现B类调用AA方法的代码:
【1】可以看见我们使用”delegate“关键字来声明委托。这个委托只有方法的原型即返回值类型、参数类型。
【2】然后编写我们想要实现的具体方法。即想要在B类调用的A类的AA方法。
【4】我们将得到的“委托变量=具体方法”关联起来。(如果有多个具体方法需要关联的话,从第二个开始要使用+=进行关联。)
【5】使用委托变量调用具体方法。(将委托变量像方法一样使用。)
public class ClassA
{
ClassB classB=new ClassB();
public void ATest()
{
classB.BB();
//【4】关联委托(委托变量关联具体方法)
classB.delegateA = AA;
//【5】使用委托变量调用方法
classB.delegateA();
}
//【2】编写具体方法
public void AA()
{
Console.WriteLine("这是类A的AA方法");
}
}
//【1】声明委托类型
public delegate void DelegateA();
【3】接下来定义委托变量。因为委托变量与具体方法不属于同一个类,如果定义成字段则只能定义成公有字段(不建议)。所以最后定义为属性。
public class ClassB
{
//ClassA classA = new ClassA();
//public void BTest()
//{
// classA.AA();
//}
public void BB()
{
Console.WriteLine("这是类B的BB方法");
}
//【3】定义委托变量
public DelegateA delegateA { get; set; }
}
static void Main(string[] args)
{
ClassA classA=new ClassA();
classA.ATest();
//ClassB classB=new ClassB();
//classB.BTest();
Console.ReadLine();
}
最后执行的结果:
这是类B的BB方法
这是类A的AA方法
3、委托使用基本步骤
从上面的例子中我们可以看出委托的使用的基本步骤有五步。
【1】声明委托类型。(定义方法的原型。一般委托都声明在类的外面,委托本身也是一种特殊的数据类型。)
【2】编写具体方法。(符合方法的原型。方法编写在类内。)
【3】创建委托变量。(字段、属性都可以,根据情况来做。)
【4】关联委托。(将一个或多个具体的方法与委托变量进行关联。)
【5】委托变量调用方法。(由于委托变量与具体方法进行了关联,所以调用时不需要再直接调用方法,直接调用委托变量即可。)
多路委托:一个委托变量关联了若干方法。当调用委托变量的时候,所关联的方法会按照关联的顺序依次执行。
断开关联:使用”-=“符号。
二、委托示例
1、窗体之间的逆向传值
①、主窗体同时打开3个子窗体。即主窗体调用子窗体的打开窗体的方法。
②、子窗体将信息传递到主窗体。即子窗体调用主窗体的信息展示方法。
最终想要到达的效果:
FrmMain主窗体的代码如下:
//【1】声明委托变量
public delegate void ChildDelegateMeathod(string data);
public partial class FrmMain : Form
{
public FrmMain()
{
InitializeComponent();
}
//【2】编写具体方法
public void ShowMes(string data)
{
this.txt_MainReceiveMes.Text += data+"\r\n";
}
private void btn_BuildChild_Click(object sender, EventArgs e)
{
for (int i = 1; i < 4; i++)
{
FrmChild frmChild = new FrmChild(i);
frmChild.Show();
//【4】关联委托变量
frmChild.ChildDelegate = ShowMes;
}
}
}
FrmChild子窗体代码如下:
public partial class FrmChild : Form
{
public FrmChild(int num)
{
InitializeComponent();
this.Text += "_" + num;
}
//【3】创建委托变量
public ChildDelegateMeathod ChildDelegate { get; set; }
private void btn_ChildSendMes_Click(object sender, EventArgs e)
{
//【5】使用委托变量
ChildDelegate(this.txt_ChildSendMes.Text.Trim());
}
}
总结:
【1】声明委托类型。(一般放在具体方法实现类)
【2】编写具体方法。(准备逆向调用的方法,和声明在一个类)
【3】创建委托变量。(在调用类创建)
【4】关联委托。(在具体方法类,通过对象关联)
【5】委托变量调用方法。(在调用类使用)
三、事件(Event)
1、事件的概念:
事件(Event)是一种用于通知的机制,允许一个类(发布者(pubisher))向另一个类(订阅者(subscriber))发送消息。事件基于委托实现,订阅者通过委托订阅发布者的事件,当事件被触发时,所有订阅者的方法都会被调用。
事件的本质:事件的本质就是对委托的进一步包装。
发布者(发布器)publisher:是一个包含事件和委托的定义的对象。事件和委托之间的联系也定义在这个对象中。发布者类的对象调用这个事件,并通知其他的对象。
订阅者(订阅器)subscriber:是一个接受事件并提供事件处理程序的对象。在发布者(publisher)类中的委托调用订阅者(subscriber)类中的方法(事件处理程序)。
2、事件的基本步骤与委托的基本步骤对比
事件 | 委托 |
---|---|
【1】声明委托类型 | 【1】声明委托类型 |
【2】声明事件 | 【2】编写具体方法 |
【3】触发事件 | 【3】创建委托变量 |
【4】订阅事件 | 【4】关联委托 |
【5】处理事件 | 【5】委托变量调用方法 |
事件的基本步骤
【1】定义委托类型:声明一个委托类型。(与委托【1】一样)
//【1】声明委托变量
public delegate void ChildDelegateMeathod(string data);
【2】声明事件:在类中声明一个事件,类型为前面的定义的委托类型。(在委托的【3】委托类型前面加上关键字“event”。)
//【2】声明事件(even关键字)
public event ChildDelegateMeathod ChildDelegate;
【3】触发事件:在类的方法中,当某个条件满足时,触发事件。【发布器(pubisher)】(与委托的【5】一样)
private void btn_ChildSendMes_Click(object sender, EventArgs e)
{
//【3】触发事件(发布器)
ChildDelegate(this.txt_ChildSendMes.Text.Trim());
}
【4】订阅事件:在另一个类中,订阅发布者的事件。(与委托的【4】关联委托一样,事件的关联需要“+=”,)
private void btn_BuildChild_Click(object sender, EventArgs e)
{
for (int i = 1; i < 4; i++)
{
FrmChild frmChild = new FrmChild(i);
frmChild.Show();
//【4】订阅事件(使用+=进行关联)
frmChild.ChildDelegate += ShowMes;
}
}
【5】处理事件:实现事件处理程序(方法)。【订阅器(subscriber)】(与委托的【2】一样)
//【2】处理事件(订阅器)
public void ShowMes(string data)
{
this.txt_MainReceiveMes.Text += data+"\r\n";
}
最终实现的效果与上面委托实现窗体相互调用传值一样。
3、事件与委托的对比
【1】事件无法直接在“外面”赋值,比如“对象.事件=null”;会出现编译错误,而委托可以。
好处:避免用户对事件直接操作,比如Click事件,如果允许Click=null,会把底层代码清除!事件可以起到保护作用,而委托太“开发”。
【2】、event对象没有invoke()方法。
4、事件与委托的选择
【1】正常解决问题,使用事件与委托没有区别。所有,一般建议使用委托。
【2】如果做控件的二次开发,扩展控件的事件,必须使用事件。