c#委托是一个类,可以定义一种方法类型,将有这种类型的函数当做参数进行传递,即他是一个可以把方法作为参数的类。
这里通过一个小功能分别说明一下c#委托(delegate、Action、Func、predicate)的用法。
如下图所示:
Form1窗体里有两个进度条,点击start按钮,他们同时进行读取,这里就需要用到多线程。因为两个进度条如果在一个线程内,只能一个一个的读取。而用到多线程就要跨线程访问控件,这里最简单方法就是调用允许跨线程访问控件的方法,虽然一句话就可以解决,但是在复杂的程序中会造成一些莫名其妙的错误,所以用委托可以解决这个问题。
1. delegate
这里先用委托使两个进度条可以逐个读取。然后在用跨线程委托的方式使他们可以同时进行读取。
先定义一种委托类型并声明委托对象:
public delegate void SetProgressBar(int value);
SetProgressBar setProgressBar;
通俗的讲, setProgressBar是一个可以传递带一个int类型参数函数的委托。那就可以定义两个带int类型参数的函数,来让setProgressBar当参数传递即可。
private void setProgressValue1(int value)
{
progressBar1.Value = value;
}
private void setProgressValue2(int value)
{
progressBar2.Value = value;
}
上面是分别将vlue的值赋给progressBar1和progressBar2的value属性。
然后就可以将上面两个函数当作参数传递给setProgressBar委托。那么怎样将这两个函数传递给setProgressBar委托人呢,这里需要将这两个函数注册绑定给setProgressBar委托即可。
private void btnStart_Click(object sender, EventArgs e)
{
setProgressBar = new SetProgressBar(setProgressValue1); //绑定方法1
setProgressValueMethod(setProgressValue1);
setProgressBar += setProgressValue2; //绑定方法2
setProgressValueMethod(setProgressValue2);
}
上面将方法一注册绑定给了setProgressBar 委托,并调用了setProgressValueMethod方法,然后将setProgressValue2方法注册绑定给了setProgressBar ,再次调用setProgressValueMethod方法。那么,既然已经将两个函数绑定给了setProgressBar 委托,执行这两个函数的方法就可以交给setProgressBar 了,setProgressValueMethod方法里有setProgressBar 怎样给这两个函数办事情的代码:
private void setProgressValueMethod(SetProgressBar setProgressBar)
{
for (int i = 0; i <= 100; i++)
{
Application.DoEvents(); //可以处理其他事件,比如拖动窗体等
Thread.Sleep(50);
setProgressBar(i); //当参数使用。
}
}
这里在一个循环体力调用委托,委托里的参数就是绑定函数的参数,执行 setProgressBar(i),相当于执行了绑定在该委托上的那两个函数,他们的执行顺序当然是谁先注册就先执行谁。
当然,用一般的方法也可以让这两个进度条逐一读取,但是如果是让两个进度条同时读取,并且在拖动窗体的时候,进度条还可以继续读取呢。这里就要用到跨线程访问控件。
private void setProgressBarMethod(int value)
{
progressBar2.Value = value;
}
private void setProgressBarValue()
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(50);
progressBar2.Invoke(setProgressBar, i); //委托给该控件
}
}
private void btnStart_Click(object sender, EventArgs e)
{
setProgressBar = new SetProgressBar(setProgressBarMethod);
Thread SetProgress = new Thread(setProgressBarValue); //线程调用该方法
SetProgress.Start();
for (int i = 0; i <= 100; i++)
{
Application.DoEvents();
Thread.Sleep(50);
progressBar1.Value = i;
}
}
这里让progressBar1在主线程上运行,让progressBar2在SetProgress 线程上运行,就可以达到并行的目的。那么在SetProgress线程里我们需要改变progressBar2.value的值,而progressBar2属于主线程的控件,那么就需要跨线程访问,这里就要用到委托。即progressBar2.Invoke(setProgressBar, i);这句就是跨线程调用setProgressBar委托,第二个参数就是注册在委托上的函数的参数。
这样就可以实现连个进度条同时读取了,由于第一个进度条在主线程中,当我们拖动窗体时,该进度条会停止读取。这就是线程阻塞。
那么这样调用委托,跨线程访问控件,回调是不是很麻烦,我们可以使用其他三种委托方法以及lambda表达式使程序更加简洁:
2. Action
Action是一个无返回值的泛型委托,下面是在该小功能下的使用方式:
private void btnStart_Click(object sender, EventArgs e)
{
int i=0;
Thread SetProgress = new Thread(() => {
progressBar2.Invoke(new Action((n)=>{
for ( ; n <= 100; n++)
{
Application.DoEvents();
Thread.Sleep(50);
progressBar1.Value = n;
}}),i);
}) ;
SetProgress.Start();
for (int m = 0; m <= 100; m++)
{
Application.DoEvents();
Thread.Sleep(50);
progressBar1.Value = m;
}
}
以上程序结合Action委托和lambda表达式使程序变的很简洁。Action的参数是一个委托,前面的表示接受一个int参数的委托。这里的委托直接用lambda表达式代替了,即它是一个匿名委托。
3. Func
Func是有返回值的泛型委托,它的最后一个参数是返回类。例如:Func
Func