C#委托和事件入门详解
Posted Nemo_XP
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#委托和事件入门详解相关的知识,希望对你有一定的参考价值。
经典实例:父窗体与子控件的相互调用
这里先复习一下父类和继承子类的相互调用.
class Program
{
static void Main(string[] args)
{
Student student = new Student() { Age = 9, GradeName = "四年级" };
// 子类可直接调用父类非同名成员
student.SayHello();// Show:我是Person,大家好
// 子类通过转化父类型访问父同名方法
((Person)student).SayHi();// Show:我是person,9岁
// 父类通过里氏转换调用子类成员(即虚方法实现多态):该person前提是student
Person person = (Person)student;
student.SayHi(); // Show:我是Student,在四年级
Console.Read();
}
}
public class Person
{
public int Age { get; set; }
public void SayHello()
{
Console.WriteLine($"我是Person,大家好");
}
public virtual void SayHi()
{
Console.WriteLine($"我是person,{Age}岁");
}
}
public class Student : Person
{
public string GradeName { get; set; }
public new void SayHi()// override重写/new隐藏父类方法
{
Console.WriteLine($"我是Student,在{GradeName}");
}
}
然后,考虑父窗体和子控件如何相互调用成员
父窗体调用子控件很简单:
应用场景, 父窗体刷新的时候,让子控件的列表数据也刷新一下.
public class FormA
{
public void Load()
{
ControlB controlB = new ControlB();
FormA.Panel1.Add(controlB);
// 因为子控件B为A的成员,只要将它的成员改为public,即可直接调用
formA.controlB.FuncB();
}
}
public class ControlB
{
public void FuncB() { Console.WriteLine("做了一堆事情"); }
}
子控件如何调用父窗体成员:
应用场景:子控件保存数据后,希望父窗体内的列表刷新一下.
杀马特写法:
public class FormA
{
public void Load()
{
ControlB controlB = new ControlB(this);// 把自己传过去,就可以让子控件直接使用了
FormA.Panel1.Add(controlB);
}
public void FuncA() { Console.WriteLine("做了两堆事情"); }
}
public class ControlB
{
FormA formBoss;
public ControlB(FormA formA)
{
formBoss = formA;
}
public void DoSomethingAfterSaved()
{
formBoss.FuncA();// 父窗体某个刷新
}
}
委托正常写法(包含委托的理解)
子控件的委托=我的方法
namespace ConsoleApp5
{
public delegate void DeleFunc();// 我是一个没参数的委托类型
public class FormA
{
public void Load()
{
ControlB controlB = new ControlB();
FormA.Panel1.Add(controlB);
controlB.deleF = FuncA;// (我把方法甩(委托)给你了,你咋用都行,别烦我了)
}
public void FuncA() { Console.WriteLine("做了两堆事情"); }
}
public class ControlB
{
public DeleFunc deleF;// 我这个委托实体,已被别人赋值了一套方法
public void DoSomethingAfterSaved()
{
// 保存后,调用父窗体的刷新的
deleF(); deleF(); deleF(); deleF(); deleF();// 别人给我的,我可以随意劳役这个方法
}
}
}
所以为什么使用委托:我们访问不到的类,想使用他的方法(饭),可以给他一个委托(碗),让他把方法赋值给委托(把饭盛到碗里),这个委托(碗)是引用类型,我和你都访问到.
委托的别的用法(委托(方法的载体)作为参数)
public class ClassA
{
public void Load()
{
DeleFunc deleF;// 我这个委托实体
ClassB cb = new ClassB();
deleF = cb.FuncA;// (你把方法甩(委托)给我了,什么时候用取决于我)
cb.Do(deleF);// 你喝什么由我做主
}
}
public class ClassB
{
public void Do(DeleFunc funcRandom)
{
Console.WriteLine("我好无聊,不知道该干啥.老弟,你来安排我");
funcRandom();
Console.WriteLine("喝饱了,回家");
}
public void FuncA() { Console.WriteLine("我端起了一杯水,做了一些事喝了的方法"); }
public void FuncB() { Console.WriteLine("我起了一瓶酒,喝了的方法"); }
}
所以为什么使用委托:在我们编写程序的时候,程序的上下文是固定的,在某个关键的部分,不确定调用哪个函数,这时候需要让程序更换被调用函数,又不想两个函数耦合性太高,使用委托可实现间接调用,或者可替换调用.
有了委托了,特别方便了.但是为什么还要有事件,是委托有什么缺点么?
为什么要有事件?
看下面的例子
namespace 为什么使用事件
{
public delegate void deleClick();
class Sanlianji : Button // 定义一个可以点三下的按钮
{
int i = 0;
//public event deleClick dc;
public deleClick dc;
protected override void OnClick(EventArgs e)// 按钮点三下后, 开始执行别人传过来的委托(即点三下后,程序员想让我弄啥嘞)
{
i++;
if (i == 3)
{
//执行委托
dc();
i = 0;
}
}
}
}
namespace 为什么使用事件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
sanlianji1.dc= func;// 点三下后,把func(点三下要做的事,赋值给委托),然后就实现了:点三下,弹出"你点击了三下特殊的按钮"
}
private void func()
{
MessageBox.Show("你点击了三下特殊的按钮");
}
private void button1_Click(object sender, EventArgs e)
{
// 虽说实现了我们想要的功能,可是如果我们拉个普通按钮,直接调用委托(委托是公开的,啥时候谁想掉谁调用)如下
sanlianji1.dc();// 出问题了,人家点三下才能弹出提示的美女(自定义按钮的功能),被一个猪(系统按钮)给拱了.还有可能被千千万万的猪任何时候都给拱了.
}
}
}
如上,所以我们做出来的委托,只想在我们想要的情况下调用,不能瞎调乱调, 因此,微软给我们弄了一个event关键词,在声明委托实体的时候加上event这个标志,就说明,这个委托比较害羞,藏在屋子里,外面的人想要给她沟通,必须按她的规矩来,通过+=赋值,-=取消委托
代码调整如下
namespace 为什么使用事件
{
public delegate void deleClick();
class Sanlianji : Button
{
int i = 0;
public event deleClick dc;// 加上event后,就变成了事件(特殊的委托,不能随意=(盗用),只能通过+=(赋值)或-=(取消委托))
protected override void OnClick(EventArgs e)
{
i++;
if (i == 3)
{
//执行委托
if(dc!=null)// 可以通过这么写,来得到什么时候我们三点击是否会执行方法的效果
{
dc();
}
i = 0;
}
}
}
}
namespace 为什么使用事件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
sanlianji1.dc = func;// 报错;通过赋值的方式赋值事件(特殊委托),不行了
sanlianji1.dc += func;// 只能通过+=赋值了
}
private void func()
{
MessageBox.Show("你点击了三下");
}
private void button1_Click(object sender, EventArgs e)
{
sanlianji1.dc();// 报错;通过直接调用事件(特殊委托),不行了
}
}
}
事件是什么样子的?
用我们经常见到事件的窗体举例
首先,系统命名了一个很标准的两参委托类型EventHandler
然后,系统定义了一个事件,规定了啥时候触发,啥时候调用这个委托
然后是赋值如下,
this.button1.Name = "button1";
// 右侧是正常的委托或者方法,左侧是事件,左侧限制了只有button1被点击了才能展示运行我们+=赋值的方法.
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += this.button1_Click;//这样也行的,方法赋值给委托嘛
所以,事件三小模块(都以按钮单击为例)
0.声明委托类型,创建委托实例(如上两张图)
1.调用体(什么时候调用)
如控制台Main方法中调用,按钮单击底层判断if(双击){写逻辑和调用体},或者我们自己的代码中写调用体
写法:
//…其他逻辑代码…
if (Click!=null)
{
Click();
}
//…其他逻辑代码…
2.执行内容体(符合单击条件后执行什么内容)
public void button1_Click(object sender,EventArgs e)
{
//…执行的个人代码逻辑内容…
}
3.事件赋值体(-=取消事件)
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += this.button1_Click;
而常见窗体事件,再双击后,仅需写执行内容体即可。
那日常生活中,事件怎么使用呢,我们怎么制造某种情况的事件呢
看下面这个例子:
namespace 委托与事件
{
public delegate void CatShotEventHandler();// 委托类型
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat("Tom");
Mouse mouse1 = new Mouse("Jerry");
Mouse mouse2 = new Mouse("Jack");
// 以下两行是事件赋值体
cat.CatShot += new CatShotEventHandler(mouse1.Run);// 猫来了,老鼠要做的事儿
cat.CatShot += new CatShotEventHandler(mouse2.Run);//
cat.shout();// 正常调用,以下两行是委托例子,不用看
// CatShotEventHandler dele_catShout = new CatShotEventHandler(cat.shout);// 定义了一个正常的委托,赋值的方法为:猫来了
//dele_catShout();// 调用这个委托方法,猫来了.方法内,如果已经给事件赋值,就执行赋值的方法,不执行.
Console.ReadKey();
}
//static void cat_CatShot()
//{
// throw new NotImplementedException();
//}
}
class Cat
{
private string name;
public Cat(string name)
{
this.name = name;
}
// 定义了一个事件(其实啊,就是定义了一个特殊的委托变量:特殊是指:不能随意=(盗用),只能通过+=(赋值)或-=(取消))
public event CatShotEventHandler CatShot;
public void shout()//调用体
{
Console.WriteLine("喵喵,我是{0}",name );
// 平时只是猫来,老鼠做什么不知道, 只有后来赋值了事件方法,猫来老鼠才逃.工作中的例子.平时双击的时候,我们想让系统执行我们造的特殊方法,但是有时候又不想,所以在双击事件里如此行写, 然后在需要双击执行我们方法之前+=,之后-=
if (CatShot !=null)
{
CatShot();
}
}
}
class Mouse
{
private string name;
public Mouse(string name)
{
this.name = name;
}
// 执行内容体
public void Run()
{
Console.WriteLine("老猫来了,{0}快跑",name);
}
}
}
关于事件的自定义参数,如下
private void tabControlMain_SelectedPageChanged(object sender, DevExpress.XtraTab.TabPageChangedEventArgs e)
这些参数,我们可以用自定义类, 并如下调用
if (PersonSelectChanged != null && _PickingInvokeMethod) { PersonSelectChanged(null, new PersonSelectEventArgs() { Persons = GetSelectedPerson }); };
以上是关于C#委托和事件入门详解的主要内容,如果未能解决你的问题,请参考以下文章
《C#零基础入门之百识百例》(七十六) 委托事件实例练习1 -- 猫捉老鼠
《C#零基础入门之百识百例》(七十七) 委托事件实例练习2 -- 刘备招亲甘露寺