C# 委托 Invoke用法

1.委托定义匿名内部类
讲匿名内部类前,先讲一下Invoke
Invoke的中文解释是唤醒,它有两种参数类型我们这里只讲一种即(Delegate, Object[])
Delegate就是前面提到的那个代理,而Object[]则是用来存放Delegate所代理函数的参数
MSDN上关于INVOKE方法有如下说明:在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。
用通俗的话讲就是利用控件的INVOKE方法,使该控件所在的线程执行这个代理,也就是执行我们想对 控件进行的操作,相当于唤醒了这个操作;

接着讲解一下如何利用委托实现线程改变控件的外观,确保不发生线程冲突。

在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显 示“关闭”,初学者往往会想当然地这么写:

   void ButtonOnClick(object sender,EventArgs e)
   {
         button.Text="关闭";
    }

这样的写法运行程序之后,可能会触发异常,异常信息大致是“不能从不是创建该控件的线程调用它”。注意这里是“可能”,并不一定会触发该种异常。造成这种异常的原因在 于,控件是在主线程中创建的(比如this.Controls.Add(…)😉,进入控件的事件响应函数时,是在控件所在的线程,并不是主线程。在控件的事件响应函数中改变控件的状态,可能与 主线程发生线程冲突。如果主线程正在重绘控件外观(Main.refresh()),此时在别的线程改变控件外观,就会造成画面混乱。不过这样的情况并不总会发生,如果主线程此时在重绘别的控件,就可能逃过一劫,这样的写法可以正常通过,没有触发异常。

正确的写法是在控件响应函数中调用控件的Invoke方法(其实如果大家以前用过C++ Builder的话,也会找到类似Invoke那样的激活到主线程的函数)。Invoke方法会顺着控件树向上搜索,直到找到创建控件的那个线程(通常是主线程),然后进入那个线程改变控件的外观,确保不发生线程冲突。正确写法的示例如下:

void ButtonOnClick(object sender,EventArgs e)
{
    button.Invoke(new EventHandler(delegate
    {
        button.Text="关闭";
    }));
}

这样的写法有一个烦人的地方:对不同的控件写法不同。对于TextBox,要TextBoxObject.Invoke,对于Label,又要LabelObject.Invoke。有没有统一一点的写法呢?
主窗口类本身也有Invoke方法。如果你不想对不同的控件写法不一样,可以全部用this.Invoke:

   void ButtonOnClick(object sender,EventArgs e)
  {
      this.Invoke(new EventHandler(delegate
      {
          button.Text="关闭";
      }));
  }

使用lamda表达式简化委托
在C# 3.0及以后的版本中有了Lamda表达式,NET Framework 3.5及以后版本更能用Action封装方法。例如以下写法可以看上去非常简洁:

void ButtonOnClick(object sender,EventArgs e)
{
    this.Invoke(new Action(()=>
    {
        button.Text="关闭";
    }));
}

2.this.Invoke和this.BeginInvoke的区别

private void button1_Click(object sender, EventArgs e)       
 {  
   this.textBox1.Text = "1";  
     this.Invoke(new EventHandler(delegate       
     {  
            this.textBox1.Text += "2";  
     }));  
     this.textBox1.Text += "3";  
}  

结果为:123

private void button1_Click(object sender, EventArgs e)       
 {  
  this.textBox1.Text = "1";  
        this.BeginInvoke(new EventHandler(delegate       
        {  
            this.textBox1.Text += "2";  
         })); 
         this.textBox1.Text += "3";  
}  

结果为:132

结论:
1、Invoke会阻止当前主线程的运行;BeginInvoke不会阻止当前主线程的运行,而是等当前主线程做完事情之后再执行BeginInvoke中的代码内容。
2、这2个方法都是由主线程运行的,并不是异步执行,如果代码耗时过长,同样会造成界面卡死。

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值