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

c#委托是一个类,可以定义一种方法类型,将有这种类型的函数当做参数进行传递,即他是一个可以把方法作为参数的类。

这里通过一个小功能分别说明一下c#委托(delegate、Action、Func、predicate)的用法。

如下图所示:
浅谈c#委托的四种用法及lambda匿名委托_第1张图片

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 表示传入参数为object、string, 返回值为int的委托
Func必须有返回值,不可void,最多16个参数
Func的使用方式与Action类似。

4. predicate
predicate 是返回bool型的泛型委托
predicate 表示传入参数为int 返回bool的委托
Predicate有且只有一个参数,返回值固定为bool,使用方法与Action类似。

你可能感兴趣的:(C#)