委托(Delegate)就是把方法(方法名)作为方法的参数,例如:
using System;using System.Collections.Generic;using System.Text;namespace TestDemo{ public delegate void Delegate(int arg); class Program { private static void FunctionX2(int x) { Console.WriteLine(x*x); } private static void FunctionX3(int x) { Console.WriteLine(x*x*x); } private static void Action(int x, Delegate function) { function(x); } static void Main(string[] args) { Action(2, FunctionX2); Action(2, FunctionX3); Console.ReadKey(); } }}
委托可以简化代码,但是更大的意义是在一个线程中调用另一个线程的代码,先来看一个程序。
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;namespace WinFormTest{ public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void Print(int n) { for (int i = 1; i <= n; i++) { Output.AppendText(i.ToString() + Environment.NewLine); } } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; int n = int.Parse(Input.Text); Print(n); Show.Enabled = true; } }}
当输入100的时候,显示框能迅速显示,1000的时候显示开始出现停顿,10000的时候,主窗口明显会卡住,直到输出完成。怎么样让界面不被卡住呢?答案就是用多线程。
以下就是经过多线程改进的代码。
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private int n; public MainForm() { InitializeComponent(); } private void Print() { for (int i = 1; i <= n; i++) { Output.AppendText(i.ToString() + Environment.NewLine); } } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
运行时出现如下错误
在子线程中无法访问UI线程的对象,这个时候代理就该出场了。以下是用代理实现后的代码
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private delegate void UiAction(string text); private int n; public MainForm() { InitializeComponent(); } private void Print() { UiAction uiAction = new UiAction(AppendText); for (int i = 1; i <= n; i++) { Output.Invoke(uiAction, i.ToString() + Environment.NewLine); } } private void AppendText(string text) { Output.AppendText(text); } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
运行之后,即使是10000,Output的Text在不停的变化,主界面仍然处于活动状态,可以拖动或执行其他操作。再来看一下代码的另一种形式
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private delegate void UiAction(string text); private int n; public MainForm() { InitializeComponent(); } private void Print() { UiAction uiAction = new UiAction(delegate(string text) { Output.AppendText(text); }); for (int i = 1; i <= n; i++) { Output.Invoke(uiAction, i.ToString() + Environment.NewLine); } } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
执行正常,这是匿名委托的写法,匿名委托还可写成这样
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private delegate void UiAction(); private int n; public MainForm() { InitializeComponent(); } private void Print() { for (int i = 1; i <= n; i++) { Output.Invoke(new UiAction(delegate() { Output.AppendText(i.ToString() + Environment.NewLine); })); } } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
现在我们再来引入一个概念Action,先看代码
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private int n; public MainForm() { InitializeComponent(); } private void Print() { for (int i = 1; i <= n; i++) { Actionaction = AppendText; Output.Invoke(action, i.ToString() + Environment.NewLine); } } private void AppendText(string text) { Output.AppendText(text); } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
Action其实就是委托,Action不带返回,但是可以带参数(形如Action,Action<T>,Action<T1,T2>,Action<T1,T2,T3>,Action<T1,T2,T3,T4>),如果用Lambda表达式来写的话代码就应该是这样
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private int n; public MainForm() { InitializeComponent(); } private void Print() { for (int i = 1; i <= n; i++) { Output.Invoke(new Action((string text) => Output.AppendText(text)), i.ToString() + Environment.NewLine); } } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
也可以写成这样的无参形式
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { private int n; public MainForm() { InitializeComponent(); } private void Print() { for (int i = 1; i <= n; i++) { Output.Invoke(new Action(() => Output.AppendText(i.ToString() + Environment.NewLine))); } } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; n = int.Parse(Input.Text); new Thread(new ThreadStart(Print)).Start(); Show.Enabled = true; } }}
如果把线程代码也用Lambda形式表达,代码应为这样
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; new Thread(() =>{ for (int i = 1; i <= int.Parse(Input.Text); i++) { Output.Invoke(new Action(() => Output.AppendText(i.ToString() + Environment.NewLine))); } }).Start(); Show.Enabled = true; } }}
相比上一种形式,上面这段代码连参数n都省略了,个人认为,Action配上Lambda可以巧妙的把一段实际执行的代码作为类似‘参数’的形式直接执行,这里还稍微引述一个问多线程的问题,如果代码写成这样
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void Show_Click(object sender, EventArgs e) { Show.Enabled = false; Output.Text = ""; new Thread(() =>{ Output.Invoke(new Action(() => { for (int i = 1; i <= int.Parse(Input.Text); i++) { Output.AppendText(i.ToString() + Environment.NewLine); } })); }).Start(); Show.Enabled = true; } }}
执行结果会是主界面会卡住,原因就是‘10000次操作’作为线程的‘1次操作’在主界面线程上执行了,而invoke到主界面线程执行的时候是会占用主线程的执行时间的,这样的写法跟没有用线程差不多,小区别就是用线程的时候Show.Enabled = true代码能马上执行。
Func跟Action类似,不过Func可以带返回,如
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace WinFormTest{ public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void Show_Click(object sender, EventArgs e) { Output.AppendText(DoSomething(new Func((int n) => n * n))); Output.AppendText(DoSomething(new Func ((int n) => n * n * n))); } private string DoSomething(Func func) { int n = int.Parse(Input.Text); if (n % 2 == 0) { return "您输入的是偶数,执行结果为:" + func(n) + Environment.NewLine; } else { return "您输入的是奇数,执行结果为:" + func(n) + Environment.NewLine; } } }}