近期和几位做嵌入式开发的朋友闲聊过程中, 一位朋友抱怨到: 这 C# 太难用了, 我想在 N 个窗体 (或者是 N 个用户组件之间) 传递值都搞不定, 非得要定义一个全局变量来存储, 然后用定时器来刷新值, 太 Low 了. 我急切的回答道: 这很简单, 不就是委托的事嘛. 那你来一个示例啊: 朋友道. 此为这篇博客的起因, 所以此篇博客对于有 c# 开发经验的伙伴们那是小菜一喋.
一, 对委托的理解
委托: 同一个功能, 可以根据不同的场景委托给不同的方法具体执行; 举个栗子: 某位美食爱好妹子, 通常自己做美食; 找到男票后, 就男票做美食; 换男票后, 就第二任男票做美食. 我们把这例子委托抽象化:
定义一个委托功能: 做美食; 规范及流程: 输入 "食材", 通过 "做美食" 的委托, 输出 "美食".
委托实现之自己做: 妹子自己做美食
委托实现之一号男票做: 一号男票做美食
委托实现之二号男票做: 二号男票做美食
做美食这项功能, 被妹子在不同的时间段分配给了不同的对象, 虽然妹子, 男一, 男二做的美食都很好吃, 但味道肯定有区别. 这就是委托生活化的示例, 各位看观了解否(偷笑).
二, 代码实现
上面的示例如何用代码实现, 这里就不展示了(真的很简单). 下面我们换一个稍有难度和实际应用的示例, 需求说明: 主窗体显示一个列表, 子窗体增加数据(不关闭子窗体的情况下), 主窗体列表自动更新, 且第二个子窗体打开后, 窗体内的列表也要同时更新.
UI 设计: 一个主窗体, 两个子窗体(A 窗体: 增加数据, B 窗体: 显示数据), 一个用户组件(列表显示内容)
2.1 EventBus 实现
代码如下:
- public class EventBus<T>
- {
- private List<T> list = new List<T>();
- public event EventHandler<EventBusArg<List<T>>> EventNotice;
- public delegate void DelegateItemInfoEvent(List<T> items);
- public void Add(T item)
- {
- this.list.Add(item);
- this.TriggerEventNotice();
- }
- public void Remove(T item)
- {
- this.list.Remove(item);
- this.TriggerEventNotice();
- }
- public List<T> GetAll()
- {
- return this.list;
- }
- private void TriggerEventNotice()
- {
- if (this.EventNotice != null)
- {
- this.EventNotice.Invoke(this, new EventBusArg<List<T>>()
- {
- Data = this.GetAll()
- });
- }
- }
- }
- public class EventBusArg<T> : EventArgs
- {
- public T Data { get; set; }
- }
重点:
1. 定义了一个委托类型: DelegateItemInfoEvent(List<T>items)
2. 定义了一个事件对象: EventHandler<EventBusArg<List<T>>>
3. 事件对象的参数必须继承 EventArgs 对象
4. 事件依赖委托
2.2 主窗体
代码如下:
- private EventBus<ItemInfo> eventBus = new EventBus<ItemInfo>();
- private EventBus<ItemInfo>.DelegateItemInfoEvent FunItem;
- public Form1()
- {
- InitializeComponent();
- this.eventBus.EventNotice += EventBus_EventNotice;
- }
- private void EventBus_EventNotice(object sender, EventBusArg<List<ItemInfo>> e)
- {
- if (this.ucList1.InvokeRequired)
- {
- FunItem = new EventBus<ItemInfo>.DelegateItemInfoEvent(RefreshItem);
- this.ucList1.Invoke(FunItem, e.Data);
- }
- else
- {
- this.RefreshItem(e.Data);
- }
- }
- private void RefreshItem(List<ItemInfo> item)
- {
- var ls = this.eventBus.GetAll();
- this.ucList1.LoadData(ls);
- }
重点:
1. 捕获事件: this.eventBus.EventNotice +=EventBus_EventNotice;
2. 事件处理方法中, 需要判断是否为 UI 线程引发, 如果不是, 则需要委托来进行切换线程, 代码见: private void EventBus_EventNotice(object sender, EventBusArg<List<ItemInfo>>e) 方法
3. 其中 FunItem 是委托类型的变量, 其最终的实现为 RefreshItem 方法
2.3 A 窗体: 增加数据
代码如下:
- private EventBus<ItemInfo> eventBus;
- public Form2(EventBus<ItemInfo> eventBus)
- {
- this.eventBus = eventBus;
- InitializeComponent();
- }
- private void button1_Click(object sender, EventArgs e)
- {
- // 在 UI 线程
- this.eventBus.Add(new ItemInfo()
- {
- Title = textBox1.Text,
- Val = Int32.Parse(textBox2.Text)
- });
- }
- private void button2_Click(object sender, EventArgs e)
- {
- // 跨线程
- Task.Factory.StartNew(() =>
- {
- for(var i=10; i <15; i++)
- {
- this.eventBus.Add(new ItemInfo()
- {
- Title = i.ToString() + "-Title",
- Val = i
- });
- }
- });
- }
重点:
1. 传入了 EventBus 对象的实例, 此实例与主界面的 EventBus 实例为同一个[这点很重要, 发布和订阅的事件必须在同一实例上]
2. button2_Click 事件展示的是跨线程事件, 执行此代码, 主界面的刷新会走委托
2.4 B 窗体: 订阅列表显示
代码如下:
- private EventBus<ItemInfo> eventBus;
- public Form3(EventBus<ItemInfo> eventBus)
- {
- this.eventBus = eventBus;
- InitializeComponent();
- this.eventBus.EventNotice += EventBus_EventNotice;
- }
- private void EventBus_EventNotice(object sender, EventBusArg<List<ItemInfo>> e)
- {
- if (this.ucList1.InvokeRequired)
- {
- var FunItem = new EventBus<ItemInfo>.DelegateItemInfoEvent(RefreshItem);
- this.ucList1.Invoke(FunItem, e.Data);
- }
- else
- {
- this.RefreshItem(e.Data);
- }
- }
- private void RefreshItem(List<ItemInfo> item)
- {
- var ls = this.eventBus.GetAll();
- this.ucList1.LoadData(ls);
- }
- private void Form3_FormClosing(object sender, FormClosingEventArgs e)
- {
- this.eventBus.EventNotice -= EventBus_EventNotice;
- }
重点:
1. 事件的订阅与取消订阅, 一般情况下可以在关闭窗体时取消订阅
三, 回顾
1. 事件依赖委托, 事件可以订阅和取消订阅, 其订阅就是为事件增加委托.
2. 委托的本质还是方法(或者说是函数), 只不过方法变成了一个变量, 可以在运行时动态改变
来源: https://www.cnblogs.com/cqhaibin/p/11831651.html