您的位置:首页 > 编程语言 > C#

浅谈c#委托的四种用法及lambda匿名委托

2015-11-29 22:23 441 查看
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<int>((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 <int>表示无参,返回值为类型的委托,

Func<object,string,int> 表示传入参数为object、string, 返回值为int的委托

Func必须有返回值,不可void,最多16个参数

Func的使用方式与Action类似。

4. predicate

predicate 是返回bool型的泛型委托

predicate<int> 表示传入参数为int 返回bool的委托

Predicate有且只有一个参数,返回值固定为bool,使用方法与Action类似。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: