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