深入理解C#中常见的委托

一提到委托,浮现在我们脑海中的大概是听的最多的就是类似C++的函数指针吧,呵呵,至少我的第一个反应是这样的。
关于委托的定义和使用,已经有诸多的人讲解过,并且讲解细致入微。我就不用多废话了。
今天我要说的是C#中的三种委托方式:Func委托Action委托Predicate委托以及这三种委托的常见使用场景。
Func,Action,Predicate全面解析
首先来说明Func委托,通过MSDN我们可以了解到,Func委托有如下的5种类型:


代码如下:

(1) *delegate TResult Func<TResult>();
(2)*delegate TResult Func<T1,TResult>(T1 arg1);
(3) *delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2);
(4)*delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3);
(5)*delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4);

其中(1)只能委托无参但是有返回值的函数,TResult就是其返回类型。
而(2)只能委托具有一个传入参数,有返回值的函数,T1为一个传入参数,TResult为返回类型。
(3)只能委托具有二个传入参数,有返回值的函数,T1和T2为两个传入参数,TResult为返回类型,(4)和(5)以此类推。
那么如何来使用呢? 下面给出一个简单的几个例子:


代码如下:

#region Func委托

///Func<TResult>的用法
///这里TResult代表函数的返回值类型
///只能代理返回值为TResult类型的无参函数
Func<string> func = delegate()
{
    return "我是Func<TResult>委托出来的结果";
};
Console.WriteLine(func());
Console.ReadKey();
///Func<T,TResult>的用法
///这里的T为代理的函数的传入类型,TResult代表函数的返回值类型
///只能代理参数为T类型,返回值为TResult类型的函数
Func<string, string> funcOne = delegate(string s)
{
    return s.ToUpper();
};
Console.WriteLine(funcOne("我是Func<T,TResult>委托出来的结果"));
Console.ReadKey();
///Func<T1,T2,TResult>的用法
///这里T1,T2为代理的函数的传入类型,TResult代表函数的返回值类型
///只能代理参数为T1,T2类型,返回值为TResult类型的函数
Func<string, string, string> funcTwo = delegate(string value1, string value2)
{
    return value1 + " " + value2;
};
Console.WriteLine(funcTwo("我是", "Func<T1,T2,TResult>委托出来的结果"));
Console.ReadKey();
#endregion

上面代码中,我用了匿名方法来代替函数,其中delegate()代表无参函数,delegate(string s)代表有一个传入参数的函数,以下的以此类推。

然后需要说明的就是Action委托,这个委托也是非常常用的,尤其是在涉及到线程和界面交互的时候,配合着lamada表达式使用,非常方便的实现二者的交互。后面我会提到用法。
来看看Action委托的几种表现形式:


代码如下:

(1) * delegate void Action(); 无参,无返回值
(2)* delegate void Action<T>(T1 arg1);
(3)* delegate void Action<T1,T2>(T1 arg1, T2 arg2);
(4)* delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3);
(5)* delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4);

从上面可以看出,总共有5中表现形式,其中(1)既没有传入参数,也没有返回值,那么它适合代理那些无参,无返回值的函数;(2)有一个传入参数,无返回值,适合代理有参,无返回值的函数,(3)(4)(5)以此类推。最都容纳四个传入参数。
那么如何使用呢?下面有一些简单的例子:


代码如下:

#region Action的用法
///Action<T>的用法
///这里的T为代理函数的传入类型,无返回值
Action<string[]> action = delegate(string[] x)
{
    var result = from p in x
     where p.Contains("s")
     select p;
    foreach (string s in result.ToList())
    {
        Console.WriteLine(s);
    }
};
string[] str={ "charlies","nancy","alex","jimmy","selina"};
action(str);
Console.ReadKey();
#endregion

上面的例子是通过传入的String类型的数组,找出其中包含有字符s的项,然后输出到控制台。
最后一个就是Predicate委托,这个的形式比较少一些,就是一个传入参数,返回值为bool类型,具体示例如下:


代码如下:

#region Predicate
          ///bool Predicate<T>的用法
///输入一个T类型的参数,返回值为bool类型
Predicate<string[]> predicate = delegate(string[] x)
{
    var result = from p in x
     where p.Contains("s")
     select p;
    if (result.ToList().Count > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
};
string[] _value = { "charlies", "nancy", "alex", "jimmy", "selina" };
if (predicate(_value))
{
    Console.WriteLine("They contain.");
}
else
{
    Console.WriteLine("They don't contain.");
}
Console.ReadKey();
#endregion

上面的代码其实也是判断String数组中有没有包含s的项,有的话就在控制台打印出  They contain.没有的话就打印出They don't contain.
总结一下这三个的特点就是:


代码如下:

Func可以接受0个至4个传入参数,必须具有返回值
Action可以接受0个至4个传入参数,无返回值
Predicate只能接受一个传入参数,返回值为bool类型

下面附上全部实现代码:


代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DelegateIntegrateConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
#region Func委托

///Func<TResult>的用法
///这里TResult代表函数的返回值类型
///只能代理返回值为TResult类型的无参函数
Func<string> func = delegate()
{
    return "我是Func<TResult>委托出来的结果";
};
Console.WriteLine(func());
Console.ReadKey();
///Func<T,TResult>的用法
///这里的T为代理的函数的传入类型,TResult代表函数的返回值类型
///只能代理参数为T类型,返回值为TResult类型的函数
Func<string, string> funcOne = delegate(string s)
{
    return s.ToUpper();
};
Console.WriteLine(funcOne("我是Func<T,TResult>委托出来的结果"));
Console.ReadKey();
///Func<T1,T2,TResult>的用法
///这里T1,T2为代理的函数的传入类型,TResult代表函数的返回值类型
///只能代理参数为T1,T2类型,返回值为TResult类型的函数
Func<string, string, string> funcTwo = delegate(string value1, string value2)
{
    return value1 + " " + value2;
};
Console.WriteLine(funcTwo("我是", "Func<T1,T2,TResult>委托出来的结果"));
Console.ReadKey();
/*************余下的类似上面的这种操作,最多可以接受四个传入参数***************
 *delegate TResult Func<TResult>(); 
 *delegate TResult Func<T1,TResult>(T1 arg1);
 *delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2);
 *delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3);
 *delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4);
 */
#endregion
#region Action的用法
///Action<T>的用法
///这里的T为代理函数的传入类型,无返回值
Action<string[]> action = delegate(string[] x)
{
    var result = from p in x
     where p.Contains("s")
     select p;
    foreach (string s in result.ToList())
    {
        Console.WriteLine(s);
    }
};
string[] str={ "charlies","nancy","alex","jimmy","selina"};
action(str);
Console.ReadKey();
/***************余下的类似上面的这种操作,最多可以接受四个传入参数**********
 * delegate void Action(); 无参,无返回值
 * delegate void Action<T>(T1 arg1);
 * delegate void Action<T1,T2>(T1 arg1, T2 arg2);
 * delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3);
 * delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4);
 */
#endregion
#region Predicate
///bool Predicate<T>的用法
///输入一个T类型的参数,返回值为bool类型
Predicate<string[]> predicate = delegate(string[] x)
{
    var result = from p in x
     where p.Contains("s")
     select p;
    if (result.ToList().Count > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
};
string[] _value = { "charlies", "nancy", "alex", "jimmy", "selina" };
if (predicate(_value))
{
    Console.WriteLine("They contain.");
}
else
{
    Console.WriteLine("They don't contain.");
}
Console.ReadKey();
#endregion
        }
    }
}

下面这部分主要讲解如何在WinForm中利用这些委托进行线程和界面的交互。
首先对于Func来说,由于其必须具有返回值,所以我们可以利用如下代码来实现线程和界面的交互:


代码如下:

#region 利用Func实现线程和界面交互
        private void AlternationUsingFunc(object text)
        {
//无参数,但是返回值为bool类型
this.Invoke(new Func<bool>(delegate()
{
    button1.Text = text.ToString();
    return true; //返回值
}));
        }
        private void AlternationUsingFuncThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc);
ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用");
        }
        private void button1_Click(object sender, EventArgs e)
        {
AlternationUsingFuncThread();
        }
        #endregion

其中


代码如下:

this.Invoke(new Func<bool>(delegate()
{
    button1.Text = text.ToString();
    return true; //返回值
}));

这段代码中利用了Func<TResult>这种类型,也就是没有传入参数,但是有一个bool类型的返回值,然后将这个函数利用加入到线程池中,最后运行,这里我们成功的设置了button1的text为“Func的使用”。
然后,对于Action来说,由于其可以无参,无返回值,那么它的交互方式最为简便,同时也是使用最多的,先看有参的调用方式:


代码如下:

#region 利用Action实现线程和界面交互
        private void AlternationUsingAction(object text)
        {
//需要一个T类型的参数,无返回值
this.Invoke(new Action<object>(delegate(object myText)
{
    myText = text;
    button2.Text = text.ToString();
}),text);
        }
        private void AlternationUsingActionThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction);
ThreadPool.QueueUserWorkItem(waitCallBack,"Action的使用");
        }
        private void button2_Click(object sender, EventArgs e)
        {
AlternationUsingActionThread();
        }
        #endregion

在上面的代码示例中,我们使用了带有一个传入参数的Action委托,当然了,匿名类型delegate(object myText)匿名代理了具有一个传入参数的函数。
其实简单点来说,可以像如下方式使用:


代码如下:

this.Invoke((Action)(()=>
{
    button2.Text = text.ToString();
}));

这样就显得非常的方便。
最后一个当然是Predicate委托,和上面类似,只是写起来麻烦一些,它需要一个传入参数,并且返回一个bool类型:


代码如下:

#region 利用Predicate实现线程和界面的交互
        private void AlternationUsingPrecidate(object text)
        {
//需要一个T类型的参数,返回bool类型
this.Invoke(new Predicate<object>(delegate(object myText) 
{
    myText = text;
    button3.Text = myText.ToString();
    return true;   //返回值
}),text);
        }
        private void AlternationUsingPrecidateThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate);
ThreadPool.QueueUserWorkItem(waitCallBack,"Predicate的使用");
        }
        private void button3_Click(object sender, EventArgs e)
        {
AlternationUsingPrecidateThread();
        }
        #endregion

具体的注释我已经写在代码中了,最后运行,能成功的将button3的Text置为“Predicate的使用.”
下面是全部实现代码:


代码如下:

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 DelegateIntegrateWinFormApp
{
    public partial class mainFrm : Form
    {
        public mainFrm()
        {
InitializeComponent();
        }
        private void mainFrm_Load(object sender, EventArgs e)
        {
/****************************注意例子中的使用方法****************
 * delegate TResult Func<TResult>();  无参,但是返回值为TResult类型
 * delegate void Action<T>(T1 arg1);  有一个参数arg1,但是无返回值
 * delegate bool Predicate<T>(T arg);  有一个参数arg,返回bool类型
 * **************************************************************/
        }
        #region 利用Func实现线程和界面交互
        private void AlternationUsingFunc(object text)
        {
//无参数,但是返回值为bool类型
this.Invoke(new Func<bool>(delegate()
{
    button1.Text = text.ToString();
    return true; //返回值
}));
        }
        private void AlternationUsingFuncThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc);
ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用");
        }
        private void button1_Click(object sender, EventArgs e)
        {
AlternationUsingFuncThread();
        }
        #endregion

#region 利用Action实现线程和界面交互
        private void AlternationUsingAction(object text)
        {
//需要一个T类型的参数,无返回值
this.Invoke(new Action<object>(delegate(object myText)
{
    myText = text;
    button2.Text = text.ToString();
}),text);
        }
        private void AlternationUsingActionThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction);
ThreadPool.QueueUserWorkItem(waitCallBack,"Action的使用");
        }
        private void button2_Click(object sender, EventArgs e)
        {
AlternationUsingActionThread();
        }
        #endregion
        #region 利用Predicate实现线程和界面的交互
        private void AlternationUsingPrecidate(object text)
        {
//需要一个T类型的参数,返回bool类型
this.Invoke(new Predicate<object>(delegate(object myText) 
{
    myText = text;
    button3.Text = myText.ToString();
    return true;   //返回值
}),text);
        }
        private void AlternationUsingPrecidateThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate);
ThreadPool.QueueUserWorkItem(waitCallBack,"Predicate的使用");
        }
        private void button3_Click(object sender, EventArgs e)
        {
AlternationUsingPrecidateThread();
        }
        #endregion

}
}

那么,现在对于WPF来说,该如何来使用呢?其实在WPF中,和winform中类似,只是在WPF中要实现线程和界面的交互,我们需要用Dispatcher来实现,也就是形如Control.Dispatcher.Invoke()的方式,由于与Winform实现方式无多大差别,这里我就直接附上全部代码:


代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
InitializeComponent();
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
/****************************注意例子中的使用方法****************
* delegate TResult Func<TResult>();  无参,但是返回值为TResult类型
* delegate void Action();  无参,无返回值
* delegate bool Predicate<T>(T arg);  有一个参数arg,返回bool类型
* 需要注意,与WinForm中不同的是,WPF中需要利用Control.Dispatcher.Invoke来实现,其他类似.
* **************************************************************/
        }
        #region 利用Func实现线程和界面交互
        private void AlternationUsingFunc(object text)
        {
//无参数,但是返回值为bool类型
button1.Dispatcher.Invoke(new Func<bool>(delegate()
{
    button1.Content = text.ToString();
    return true; //返回值
}));
        }
        private void AlternationUsingFuncThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc);
ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用");
        }
        private void button1_Click(object sender, RoutedEventArgs e)
        {
AlternationUsingFuncThread();
        }
        #endregion
        #region 利用Action实现线程和界面交互
        private void AlternationUsingAction(object text)
        {
//无参数,无返回值
//button2.Dispatcher.Invoke(new Action(delegate()
//{
//    button2.Content = text.ToString();
//}));
//或者
button2.Dispatcher.Invoke((Action)(()=>
{
    button2.Content = text.ToString();
}));
        }
        private void AlternationUsingActionThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction);
ThreadPool.QueueUserWorkItem(waitCallBack, "Action的使用");
        }
        private void button2_Click(object sender, RoutedEventArgs e)
        {
AlternationUsingActionThread();
        }
        #endregion
        #region 利用Predicate实现线程和界面的交互
        private void AlternationUsingPrecidate(object text)
        {
//需要一个T类型的参数,返回bool类型
this.button3.Dispatcher.Invoke(new Predicate<object>(delegate(object myText)
{
    myText = text;
    button3.Content = myText.ToString();
    return true;   //返回值
}), text);
        }
        private void AlternationUsingPrecidateThread()
        {
WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate);
ThreadPool.QueueUserWorkItem(waitCallBack, "Predicate的使用");
        }
        private void button3_Click(object sender, RoutedEventArgs e)
        {
AlternationUsingPrecidateThread();
        }
        #endregion

}
}

逐个点击界面上的按钮,我们可以看到成功实现了线程和UI的交互:

当然,上面我们只是说到了在WinForm中和WPF中如何来使用的情况,代码比较简单,也没有具体的应用场景,下面我们将结合中WPF来模拟一个具体的应用场景:
现在假设我有一个txt文档,名称为newEXO.txt,里面大概有5w行记录,文件大小为30MB左右;同时我手边还有一个oldEXO.txt里面也有5w数据,但是其中有一些记录和newEXO.txt中的不同,我现在需要对比两个txt文档,找出不同的记录,并对不同的记录进行上色操作。
那么现在这里很明确了,我们需要两个函数,一个是读取记录的函数ChangeText(),一个是上色的函数ChangeColor()。
但是在实际操作中,发现如果我直接利用wpf读取数据的话,逐行读取,耗时10s左右,也就是用户界面会被阻塞10s,然后才会显示给用户,这个体验性是相当不好的,所以拟采用异步方式来导入数据。
同时,考虑到差异比较也会比较耗时,所以也准备采用异步方式来进行对比。

那么问题来了,两个均采用异步方式进行,难免会发生数据未导入完成就开始进行差异比较的可能,所以这里还涉及到一个线程同步的问题。
现在,这里有三个操作,异步的数据导入,异步的差异比较并上色,线程同步。

首先我们看异步导入,你也可以自己实现一个异步类,通过委托的BeginInvoke方法和EndInvoke方法来实现


代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ThreadSynchorous
{
    public  class AsyncInvoke
    {
        public void BeginAsync(Func<bool> MyFunction)
        {
            Func<bool> func = new Func<bool>(MyFunction);
            IAsyncResult iar = func.BeginInvoke(new AsyncCallback(EndAsync), func);
        }
        public void EndAsync(IAsyncResult iar)
        {
            Func<bool> func = (Func<bool>)iar.AsyncState;
            func.EndInvoke(iar);
        }
    }
}

由于Action委托的使用方式最为便捷,这里我采用Action委托方式来进行,当然了,:


代码如下:

private void ChangeText()
        {
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                string filename = @"C:\newEXO.txt";
                using (StreamReader sr = new StreamReader(filename, Encoding.Default))
                {
                    string result;
                    while ((result = sr.ReadLine()) != null)
                    {
                        //here perform action
                    }
                }
                //label1.Dispatcher.Invoke((new Action(delegate()
                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") ";
                }));
            }));
        }

首先是当点击button1按钮的时候,就启动ChangeText()函数,也即数据导入函数,然后label1会在加载完毕的时候,给出提示信息。
下面再看看ChangeColor()函数:


代码如下:

private void ChangeColor()
        {
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                this.button1.Background = Brushes.Red;
                //here perform large amount of data action and color the result
                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") ";
                }));
            }));
        }

可以看到也是当button1点击的时候,会触发ChangeColor函数。由于二者操作比较耗时,为了防止用户界面阻塞,我们放到线程池中:


代码如下:

ThreadPool.QueueUserWorkItem(o => ChangeText());
ThreadPool.QueueUserWorkItem(o => ChangeColor());

从上面可以看出,当点击按钮button1的时候,两个函数同时被引发,也就是点击按钮的时候进行如下操作:


代码如下:

private void button1_Click(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(o => ChangeText());
            ThreadPool.QueueUserWorkItem(o => ChangeColor());
            label1.Content += " \r\n-------------------------\r\n";
        }

看到了什么?

看到了线程运行的混乱,我们本想让数据先加载,然后比较得出差异着色,可惜上面的结果中却与想象中的相差甚远.

这里的ChangeText()函数和ChangeColor()函数肯定不会像想象的那样顺序执行,那么代码就有问题了,所以为了避免这个问题,我们必须进行线程同步,如何来进行呢? 方法很多,这里我采用EventWaitHandle方式来进行。

EventWaitHandle的Reset方式用来重置信号量,告诉其他运行的进程,你们需要被阻塞;Set方式用来释放信号量,告诉其他运行的进程,你们的阻塞已经被解除,可以继续运行了。

但是其他进行通过什么来知道自己是否可以解除阻塞状态呢? 那就是利用WaitOne方式来判断:

也就是按照如下的代码模式来:


代码如下:

EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset);
private void ChangeText()
{
     waitMeHandle.Reset();  //即将进入下列执行过程,其他需要阻塞
  //....
     waitMeHandle.Set(); //释放
}
private void ChangeColor()
{
      waitMeHandle.WaitOne(); //等待,直到接收到Set信号,才能运行
     //  ...
}

当然上面我举出的例子只是一个Sample,我写过这个软件,利用的是BeginInvoke和EndInvoke方式实现的异步调用,有兴趣可以参见我的这篇文章中提到的软件:
下面是软件截图:

另外在写这篇文章的时候,我在StackOverFlow上面有过提问,就是关于当前的Thread的ThreadId为什么一致的问题, 应该说两个函数放到了ThreadPool中,结果出来的ThreadId应该不一样才对呀.

其实,正确的答案是我的打印出ThreadId的信息都放在了label1.Dispatcher.Invoke这句话中,而这个Lable1属于界面UI,也就是前台线程,所以ThreadId会是一样的,如果你直接在进入函数的时候,输出ThreadId,你就会发现两个函数运行在不同的线程上了.

下面是全部代码:


代码如下:

using System;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Threading;
using System.IO;
namespace ThreadSynchorous
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            asyncInvoke = new AsyncInvoke();
        }
        AsyncInvoke asyncInvoke;
        EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset);
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(o => ChangeText());
            ThreadPool.QueueUserWorkItem(o => ChangeColor());
            label1.Content += " \r\n-------------------------\r\n";
        }

private void ChangeText()
        {
            waitMeHandle.Reset();  //即将进入下列执行过程,其他需要阻塞
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                string filename = @"C:\MyLearn\eqrms_hk_20111219_listedposn_ff\EQRMS_HK_20111219_EXO.txt";
                using (StreamReader sr = new StreamReader(filename, Encoding.Default))
                {
                    string result;
                    while ((result = sr.ReadLine()) != null)
                    {
                        //here perform action
                    }
                }
                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") ";
                    waitMeHandle.Set(); //释放
                }));
            }));
        }

private void ChangeColor()
        {
            waitMeHandle.WaitOne(); //等待,直到接收到Set信号,才能运行
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                this.button1.Background = Brushes.Red;
                //here perform large amount of data action and color the result
                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") ";
                }));
            }));
        }
    }
}

本文中涉及到的源码,可以从这里下载

(0)

相关推荐

  • C#零基础学习理解委托

    说来惭愧,在大学的课程中,竟然没有听说过委托这个名称.那么今天我就带着大家一起探讨下委托和事件. 咱们先来看下委托 我主要从以下几个方面讲解 1,  为什么使用委托  2.什么是委托  3.委托如何使用 为什么使用委托? 委托是c#中非常重要的一个概念,使用委托使程序员可以将方法引用封装在委托对象内.然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法.与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的. 什么是委托? 委托是一种引用方法的类型,一旦为委

  • c# 委托和事件实例学习

    Common.cs: 复制代码 代码如下: using System; using System.Collections.Generic; using System.Text; namespace DelegateAndEvent.App_Code { public class Common { //定义全局变量. public static string txt = ""; #region 定义方法 public string HelloCSharp(string name) { t

  • c#委托与事件(详解)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在.本文中,我将通过两个范例由浅入深地讲述什么是委托.为什么要使用委托.事件的由来..Net Framework中的委托和事件.委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论. 将方法作为方法的参数 我们先不管这个标题

  • C#中委托的+=和-=深入研究

    写在前面 为什么会突然想说说委托?原因吗,起于一个同事的想法,昨天下班的路上一直在想这个问题,如果给委托注册多个方法,会不会都执行呢?为了一探究性,就弄了个demo研究下. += 大家都知道委托都继承自System.MulticastDelegate,而System.MulticastDelegate又继承自System.Delegate,可以通过+=为委托注册多个方法.那么他们是否都执行了呢?执行的结果又是怎样的呢?有返回值和没返回值的是否结果是否一样?那就试着说说+=都干了哪些事? 测试代码

  • c#委托详解和和示例分享

    什么是委托? 委托是寻址方法的.NET版本,使用委托可以将方法作为参数进行传递.委托是一种特殊类型的对象,其特殊之处在于委托中包含的只是一个活多个方法的地址,而不是数据. 委托虽然看起来像是一种类型,但其实定义一个委托,是定义了一个新的类.下面这行代码,定义了一个委托,使用ILDasm.exe查看其生成的IL代码如图所示: 复制代码 代码如下: //定义委托,它定义了可以代表的方法的类型,但其本身却是一个类 public delegate int methodDelegate(string st

  • C#委托与匿名委托详解

    本来是想写一篇<委托与lambda表达式的前世今生>,但仅委托部分已经写了很多内容,于是就此分开关于Lambda表达是的内容后续再写吧. 不知道Lambda表达式是谁发明的,只记得第一次接触Lambda表达式是在使用VS2008的时候,那就先认为是微软发明的吧. Lambda表达式从我接触开始到现在变得越来越流行,Java8中开始支持.kotlin更是对C#,F#做了广泛的抄袭(C#曾几何时不也如此对待过Java嘛).其实这都充分说明了,Lambda表达式的重要性.要搞清楚Lambda首先需要

  • C#中委托和事件的区别实例解析

    本文实例分析了C#中委托和事件的区别,分享给大家供大家参考之用.具体如下: 大致来说,委托是一个类,该类内部维护着一个字段,指向一个方法.事件可以被看作一个委托类型的变量,通过事件注册.取消多个委托或方法.本篇分别通过委托和事件执行多个方法,从中体会两者的区别. 一.通过委托执行方法 class Program { static void Main(string[] args) { Example example = new Example(); example.Go(); Console.Re

  • C# 委托的三种调用示例(同步调用 异步调用 异步回调)

    首先,通过代码定义一个委托和下面三个示例将要调用的方法: 复制代码 代码如下: public delegate int AddHandler(int a,int b);    public class 加法类    {        public static int Add(int a, int b)        {            Console.WriteLine("开始计算:" + a + "+" + b);            Thread.Sl

  • C#中自定义事件和委托实例

    在windows 编程中用到最多的就是控件的事件了,微软给我们很好的方式,把注意力放到事件执行方法的设计和编码上,但是但我们真正弄懂了事件的真正出发执行原理的话,对我们的编程的提高真是非常榜的,例如在windows编程中 如果我单击了一个button按钮触发了button 的click事件  Button1_Click(){} ,但是有时候我们编程的时候,不但想要触发button 的单击事件,我还想要把其他的时间也要调用下来顺序执行,要实现这种方式,除了在方法最后对其他方法的调用,还可以利用将其

  • C#中Predicate<T>与Func<T, bool>泛型委托的用法实例

    本文以实例形式分析了C#中Predicate<T>与Func<T, bool>泛型委托的用法,分享给大家供大家参考之用.具体如下: 先来看看下面的例子: static void Main(string[] args) { List<string> l = new List<string>(); l.Add("a"); l.Add("b"); l.Add("s"); l.Add("t&quo

随机推荐