淺談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類型

下面附上全部實現代碼:

View Code
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和WPF中,利用Func,Action,Predicate進行線程UI交互

下面這部分主要講解如何在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的使用.”

下面是全部實現代碼:

View Code
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實現方式無多大差別,這里我就直接附上全部代碼:

View Code
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方式實現的異步調用,有興趣可以參見我的這篇文章中提到的軟件:

我的作品:PlainTextCompare

下面是軟件截圖:

另外在寫這篇文章的時候,我在StackOverFlow上面有過提問,就是關於當前的Thread的ThreadId為什么一致的問題, 應該說兩個函數放到了ThreadPool中,結果出來的ThreadId應該不一樣才對呀.

其實,正確的答案是我的打印出ThreadId的信息都放在了label1.Dispatcher.Invoke這句話中,而這個Lable1屬於界面UI,也就是前台線程,所以ThreadId會是一樣的,如果你直接在進入函數的時候,輸出ThreadId,你就會發現兩個函數運行在不同的線程上了.

StackOverFlow的問答,請點擊這里

下面是全部代碼:

View Code
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 + ") ";
}));

}));
}
}
}

本文中涉及到的源碼,可以從這里下載

如果你感覺這篇文章對你有幫助,幫我點一下[推薦]哦,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM