惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
MyScale Blog
MyScale Blog
PCI Perspectives
PCI Perspectives
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
Lohrmann on Cybersecurity
C
Cybersecurity and Infrastructure Security Agency CISA
Spread Privacy
Spread Privacy
量子位
P
Proofpoint News Feed
T
Tailwind CSS Blog
腾讯CDC
G
Google Developers Blog
V
Visual Studio Blog
The GitHub Blog
The GitHub Blog
博客园 - 司徒正美
GbyAI
GbyAI
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
A
About on SuperTechFans
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
Recorded Future
Recorded Future
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
aimingoo的专栏
aimingoo的专栏
美团技术团队
Engineering at Meta
Engineering at Meta
博客园 - 【当耐特】
小众软件
小众软件
F
Fortinet All Blogs
Last Week in AI
Last Week in AI
U
Unit 42
T
The Blog of Author Tim Ferriss
B
Blog RSS Feed
云风的 BLOG
云风的 BLOG
Microsoft Azure Blog
Microsoft Azure Blog
The Cloudflare Blog
Recent Announcements
Recent Announcements
S
SegmentFault 最新的问题
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog
A
Arctic Wolf
C
CERT Recently Published Vulnerability Notes
Security Latest
Security Latest
L
LINUX DO - 热门话题

博客园 - SamDragon

如何通过LangChain实现记忆功能的总结 基于LangChain的Ai应用开发平台 .net CPU和内存爆高的分析与处理01 前端导出Excel和打印介绍 ApiTemplate:.net后端项目模板完善与总结 Jenkins之Nunit的应用 jenkins发布application且并运行 Jenkins基础系统之完整的.net项目编译 Jenkins基础系统之更换镜像源 记一次业余项目的敏捷开发实践 奉上简单的.Net后端开发模板 cordova环境搭建 Linux实现免密码登录 ElasticSearch安装及运行的坑 Autofac 应用于IIS托管的WEB程序,注册程序集被回收的问题 通过Thrift实现C#与Hbase交流 OpenLayer实现路径运动 对SVN的落地与实践总结 Jenkins配置从节点
C#之委托如此简单
SamDragon · 2019-11-10 · via 博客园 - SamDragon

     近期和几位做嵌入式开发的朋友闲聊过程中,一位朋友抱怨到:这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. 委托的本质还是方法(或者说是函数),只不过方法变成了一个变量,可以在运行时动态改变

3. 源码下载