欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#之委托如此简单

程序员文章站 2023-09-07 08:24:28
近期和几位做嵌入式开发的朋友闲聊过程中,一位朋友抱怨到:这C#太难用了,我想在N个窗体(或者是N个用户组件之间)传递值都搞不定,非得要定义一个全局变量来存储,然后用定时器来刷新值,太Low了。我急切的回答道:这很简单,不就是委托的事嘛。那你来一个示例啊:朋友道。此为这篇博客的起因,所以此篇博客对于有 ......

     近期和几位做嵌入式开发的朋友闲聊过程中,一位朋友抱怨到:这c#太难用了,我想在n个窗体(或者是n个用户组件之间)传递值都搞不定,非得要定义一个全局变量来存储,然后用定时器来刷新值,太low了。我急切的回答道:这很简单,不就是委托的事嘛。那你来一个示例啊:朋友道。此为这篇博客的起因,所以此篇博客对于有c#开发经验的伙伴们那是小菜一喋。

一、对委托的理解

     委托:同一个功能,可以根据不同的场景委托给不同的方法具体执行; 举个栗子:某位美食爱好妹子,通常自己做美食;找到男票后,就男票做美食;换男票后,就第二任男票做美食。我们把这例子委托抽象化:

    定义一个委托功能:做美食;规范及流程:输入”食材“,通过”做美食“的委托,输出”美食“。

    委托实现之自己做:妹子自己做美食

    委托实现之一号男票做:一号男票做美食

    委托实现之二号男票做:二号男票做美食

    做美食这项功能,被妹子在不同的时间段分配给了不同的对象,虽然妹子,男一,男二做的美食都很好吃,但味道肯定有区别。这就是委托生活化的示例,各位看观了解否(偷笑)。

二、代码实现

    上面的示例如何用代码实现,这里就不展示了(真的很简单)。下面我们换一个稍有难度和实际应用的示例,需求说明:主窗体显示一个列表,子窗体增加数据(不关闭子窗体的情况下),主窗体列表自动更新,且第二个子窗体打开后,窗体内的列表也要同时更新。

    ui设计:一个主窗体,两个子窗体(a窗体:增加数据,b窗体:显示数据),一个用户组件(列表显示内容)

2.1 eventbus实现

C#之委托如此简单

代码如下:

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. 委托的本质还是方法(或者说是函数),只不过方法变成了一个变量,可以在运行时动态改变

3. 源码下载