近期和几位作嵌入式开发的朋友闲聊过程当中,一位朋友抱怨到:这C#太难用了,我想在N个窗体(或者是N个用户组件之间)传递值都搞不定,非得要定义一个全局变量来存储,而后用定时器来刷新值,太Low了。我急切的回答道:这很简单,不就是委托的事嘛。那你来一个示例啊:朋友道。此为这篇博客的原由,因此此篇博客对于有c#开发经验的伙伴们那是小菜一喋。c#
委托:同一个功能,能够根据不一样的场景委托给不一样的方法具体执行; 举个栗子:某位美食爱好妹子,一般本身作美食;找到男票后,就男票作美食;换男票后,就第二任男票作美食。咱们把这例子委托抽象化:函数
定义一个委托功能:作美食;规范及流程:输入”食材“,经过”作美食“的委托,输出”美食“。ui
委托实现之本身作:妹子本身作美食this
委托实现之一号男票作:一号男票作美食spa
委托实现之二号男票作:二号男票作美食线程
作美食这项功能,被妹子在不一样的时间段分配给了不一样的对象,虽然妹子,男一,男二作的美食都很好吃,但味道确定有区别。这就是委托生活化的示例,各位看观了解否(偷笑)。设计
上面的示例如何用代码实现,这里就不展现了(真的很简单)。下面咱们换一个稍有难度和实际应用的示例,需求说明:主窗体显示一个列表,子窗体增长数据(不关闭子窗体的状况下),主窗体列表自动更新,且第二个子窗体打开后,窗体内的列表也要同时更新。code
UI设计:一个主窗体,两个子窗体(A窗体:增长数据,B窗体:显示数据),一个用户组件(列表显示内容)orm
代码以下:对象
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. 事件依赖委托
代码以下:
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方法
代码以下:
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事件展现的是跨线程事件,执行此代码,主界面的刷新会走委托
代码以下:
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. 委托的本质仍是方法(或者说是函数),只不过方法变成了一个变量,能够在运行时动态改变