使用匿名委托,Lambda简化多线程代码

 

.net中的线程也接触不少了。在多线程中最常见的应用莫过于有一个耗时的操作需要放到线程中去操作,而在这个线程中我们需要更新UI,这个时候就要创建一个委托了来更新UI了,不然会报错的。下面我们就来设计一个简单的场景:窗体上有一个按钮和进度条,按钮按下后启动一个线程让进度条滚动。需要说明一下的是,我们这里不讨论使用匿名委托,lambda的好坏,我们只有一个目标就是使得我们的程序:短点,短点,再短点。

最“朴素”写法

我刚开始没有掌握匿名方法这些就是按照下面这些写的,说实话很痛苦。后来接触了匿名表达式,lambda后几乎都不想再想写这样的东西了,除非特殊的一些情况,比如需要自己定义委托。如果您现在还在按照下面这样写,那么这篇文章对你或许有些帮助!

        //声明一个委托
delegate void UpdateProgressDelegate();
//声明一个UpdateProgressDelegate的委托实例
private UpdateProgressDelegate UpdateProgressHandle; public Form1()
{
InitializeComponent();
progressBar1.Maximum = 100;
progressBar1.Minimum = 0;
progressBar1.Value = 0;
//将该委托实例和UpdateProgressValue方法绑定起来
UpdateProgressHandle = new UpdateProgressDelegate(UpdateProgressValue);
} private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(D));
t.Start();
} private void D()
{
//其他事情
//..........
progressBar1.Invoke(UpdateProgressHandle); //调用Invoke更新进度条,参数是我们新建的委托
} //更新进度条的方法
private void UpdateProgressValue()
{
for (int i = 0; i < 50; i++)
{
progressBar1.Value = i;
}
}
去掉委托创建

这个写法基本就是.net 1.x里面的委托写法了。您也看出来,很繁琐。像一个多线程的界面中,要和多线程打交道的控件何止一个两个。几个一来就会感觉很烦了。维护起来也不方便。下面我们使用.net 2.0中的一个新特性:支持省略委托的创建,直接将方法名字赋给需要的参数。即我们可以将

Thread t = new Thread(new ThreadStart(D));

改为:

Thread t = new Thread(D);

虽然只是少了一点,不过好歹也是个进步对吧。

去掉自定义方法

现在我们引入.net 2.0中的匿名委托来改善下上面这个程序,使其看起来更加简洁点。怎样使用匿名委托?教你个简单的方法,程序中参数是方法名字的地方您都可以通过delegate(){//操作}的形式来代换。比如下面我们就像D方法名那里给替换掉。

        //声明一个委托
delegate void UpdateProgressDelegate();
//声明一个UpdateProgressDelegate的委托实例
private UpdateProgressDelegate UpdateProgressHandle; public Form1()
{
InitializeComponent();
progressBar1.Maximum = 100;
progressBar1.Minimum = 0;
progressBar1.Value = 0;
//将该委托实例和UpdateProgressValue方法绑定起来
UpdateProgressHandle = new UpdateProgressDelegate(UpdateProgressValue);
} private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(delegate() { progressBar1.Invoke(UpdateProgressHandle); });
t.Start();
} //更新进度条的方法
private void UpdateProgressValue()
{
for (int i = 0; i < 50; i++)
{
progressBar1.Value = i;
}
}
我们将D方法删除,将Thread线程声明的时候直接改为使用匿名委托来定义需要执行的操作。怎么样?整整少了一个方法的定义,同时操作更加接近使用的地方了。不过我还是觉得太多了,还能去掉一点吗?当然能。
 
去掉自定义委托
      我们在上面我们定义的UpdateProgressDelegate委托上下功夫。能不能直接不用声明就可以使用呢?这时我们就需要使用Action<T>,Func<T,ResultT>委托了。这两个是系统自带的委托,利用这两个现成的委托我们可以省去自定义简单委托的步骤。这两个委托的区别在于Action<T>只有参数没有返回值,而Func<T,ResultT>既有参数也有返回值。里面的T代表了你要执行的方法的参数类型。另外需要注意的是,在.net framework 2.0中只有Action<T>一种形式,在3.5中增加了Action(无参数形式)以及Action<T1,T2>等最多四个参数,Func<T,ResultT>,Func<T1,T2,ResultT>最多四个参数。所以如果您的.net版本是2.0那么意味着您只有Action<T>可以使用。我们现在首先在.net 3.5下用Action委托来简化上面的代码。 形式如下:
    public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
progressBar1.Maximum = 100;
progressBar1.Minimum = 0;
progressBar1.Value = 0;
} private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(delegate() { progressBar1.Invoke(new Action(UpdateProgressValue)); });
t.Start();
} //更新进度条的方法
private void UpdateProgressValue()
{
for (int i = 0; i < 50; i++)
{
progressBar1.Value = i;
}
}
}

可以看到我们之前声明的那一长段的委托都去掉了,清爽了不少。那么在.net 2中该如何使用呢?两个办法:

1.强制在UpdateProgressValue中加个参数,但我们不使用。代码如下:

代码

2.不使用Action委托,还记得我们最朴素写法中的ThreadStart这个委托吗?这个就是一个现成的无参数委托,不用白不用!代码如下:

代码

上面我们已经说过了,有方法名为参数的地方可以使用匿名方法替代,那么上面的那个new Action(UpdateProgressValue)中的UpdateProgressValue我们同样可以再给替换掉了。代码如下:

代码
 
怎么样,这下子就在一个方法里面搞定所有的事情了,这样写起来是不是比你定义许多委托啊什么的爽多了。不过我们的旅程还没有结束,还有减少的空间。
终极简化---使用Lambda

我们最后引入lambda来简化我们的代码。Lambda表达式在C#中的写法是“arg-list => expr-body”,“=>”符号左边为表达式的参数列表,右边则是表达式体(body)。参数列表可以包含0到多个参数,参数之间使用逗号分割。当然因为我们这里没有参数所有可以直接写成()=>{}的形式了啦。lambda用在哪里呢?它可以替换匿名表达式使其更加简单,在LINQ等等查询语句中也有使用,不过不是我们今天讨论的范围。如何替换匿名表达式呢?代码如下:

        private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() => progressBar1.Invoke(new Action(()=>
{
for (int i = 0; i < 50; i++)
{
progressBar1.Value = i;
}
})));
t.Start();
}

可以看到使用了lambda后匿名方法中的一些不需要的{}被省略了,自然看起来也就爽多啦。如果非要总结一个怎么替换的过程的话那就简单的认为将delegate(){}替换为了()=>{},如果有参数类似。

最后需要说的是别看我们上面的代码样子好像变化了不少,其实在编译后编译器会为我们上面省略的一系列代码再加上去的。有兴趣的可以看看简化后的IL和没有简化的IL,其实都是差不多的。好了,这就是我目前能达到的最短代码了。如果您还能再短些欢迎提出!

使用匿名委托,Lambda简化多线程代码的更多相关文章

  1. Unity C# 多态 委托 事件 匿名委托 Lambda表达式 观察者模式 .NET 框架中的委托和事件

    一.多态 里氏替换原则: 任何能用基类的地方,可以用子类代替,反过来不行.子类能够在基类的基础上增加新的行为.面向对象设计的基本原则之一. 开放封闭原则: 对扩展开放,意味着有新的需求或变化时,可以对 ...

  2. C#学习笔记(28)——匿名委托和Lambda表达式

    说明(2017-11-21 18:51:32): 1. 例子为求1~100的和,答案应该是5050(小学学算盘的时候,我爹就让我算,从1拨到100是多少呀?当时的我年幼无知,还不知道高斯小时候的故事, ...

  3. C#委托,匿名方法,Lambda,泛型委托,表达式树代码示例

    第一分钟:委托 有些教材,博客说到委托都会提到事件,虽然事件是委托的一个实例,但是为了理解起来更简单,今天只谈委托不谈事件.先上一段代码: 下边的代码,完成了一个委托应用的演示.一个委托分三个步骤: ...

  4. 委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底

    本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景.各位看官,这里就不啰嗦了,直接上代码. 首先定义一个泛型委托类型,如下: public delegate T Function&l ...

  5. 匿名委托与Lambda表达式

    通过使用匿名委托(匿名方法),使编程变得更加灵活,有关委托与匿名委托请参考我的前一篇Blog<委托与匿名委托>. 继续之前示例,代码如下: static void Main(string[ ...

  6. .net 系列:Expression表达式树、lambda、匿名委托 的使用

    首先定义一个泛型委托类型,如下: public delegate T Function<T>(T a, T b); 实现泛型委托的主体代码,并调用: public static strin ...

  7. .net 系列:Expression表达式树、lambda、匿名委托 的使用【转】

    https://www.cnblogs.com/nicholashjh/p/7928205.html 首先定义一个泛型委托类型,如下: public delegate T Function<T& ...

  8. C#委托总结-匿名方法&Lambda表达式

    1,匿名方法 匿名方法可以在声明委托变量时初始化表达式,语法如下 之前写过这么一段代码: delegate void MyDel(string value); class Program { void ...

  9. 匹夫细说C#:委托的简化语法,聊聊匿名方法和闭包

    0x00 前言 通过上一篇博客<匹夫细说C#:庖丁解牛聊委托,那些编译器藏的和U3D给的>的内容,我们实现了使用委托来构建我们自己的消息系统的过程.但是在日常的开发中,仍然有很多开发者因为 ...

随机推荐

  1. hadoop shell 详解

    概述  所有的hadoop命令均由bin/hadoop脚本引发.不指定参数运行hadoop脚本会打印所有命令的描述.  用法: hadoop [--config confdir] [COMMAND] ...

  2. strong和b

    strong和b标签都是很久以前遗留下来的标签,b标签用来加粗字体,strong用来强调,通常浏览器会把强调的语句加粗,所以二者效果比较近似.语义化愈发受重视以后,b标签退出大众视野,strong依然 ...

  3. CSS控制文本超出指定宽度后用省略号代替,CSS控制文本不换行

    CSS控制文本超出指定宽度后用省略号代替,CSS控制文本不换行. 一般的文字截断(适用于内联与块): .text-overflow {     display:block;/*内联对象需加*/     ...

  4. c 深度剖析 6

    函数的编码风格 1.注释 2,空行 3,缩进 4,参数长度,代码长度,语句长度要合适. 5,少用全局变量 6,指针仅作输入参数时,可用const 设置其为只读属性,避免其在函数中被修改. 7,函数默认 ...

  5. System program problem detected 解决

    每次开机都出现:System program problem detected 管理员权限打开:/etc/default/apport   su root   vim /etc/default/app ...

  6. 哪些函数不能为virtual函数

    1> 内联函数 内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾,不能定义内联函数为虚函数. 2> 构造函数 构造函数用来创建一个新的对象,而虚函数的运行是建立在对 ...

  7. HDU-1520 Anniversary party(树形DP)

    题目大意:一棵树,每个节点都带权.从中取出一些节点,并且子节点不能与父节点同时取,求能取得的最大值. 题目分析:定义状态dp(u,0/1)表示u点不取/取.则状态转移方程为: dp(u,1)=sum( ...

  8. 英语语法最终珍藏版笔记- 21it 用法小结

    it 用法小结 it 在英语中的意思较多,用法较广,现总结如下. 一.it作句子的真正主语 1.it 指前面已经提到过的人或事物,有时指心目中的或成为问题的人或事物,作真正主语. 例如: What’s ...

  9. Bootloader的原理以及实现(转载)

    BootLoader工作原理 BootLoader工作原理 BootLoader指系统启动后,在操作系统内核运行之前运行的一段小程序.通过BootLoader,我们可以初始化硬件设备.建立内存空间的映 ...

  10. java基础之:匿名内部类

    在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以 了解到匿名内部类的使用.匿名内部类要注意的事项.如何初 ...