一、委托
基本用法:
1.聲明一個委托類型。委托就像是‘類'一樣,聲明了一種委托之后就可以創建多個具有此種特征的委托。(特征,指的是返回值、參數類型)
public delegate void SomeKindOfDelegate(string result);
2.創建一個在1中創建的委托類型的委托。
public SomeKindOfDelegate aDelegate;
3.為2中創建的具體的委托添加響應函數。響應函數必須符合1中的‘特征'。
aDelegate +=new SomeKindOfDelegate(aFunctionThatJustForDelegate); private void aFunctionThatJustForDelegate(string result) { MessageBox.Show(result); }
4.完成以上三步之后,就可以使用Invoke來對委托進行調用了。Invoke可以選擇調用的目標函數,調用優先級,以及調用的參數。
aDelegate.BeginInvoke("Hello~I'm being invoked!", null, null);
上面是基本用法,除了這種基本用法之外,還可以結合var、匿名委托、lambda委托等方法。
完整代碼:
namespace wtfIsDelegate { public delegate void SomeKindOfDelegate(string result); public partial class Form1 : Form { public event SomeKindOfDelegate aDelegate; public Form1() { InitializeComponent(); aDelegate +=new SomeKindOfDelegate(aFunctionThatJustForDelegate); aDelegate.BeginInvoke("Hello~I'm being invoked!", null, null); } private void btnDelegate_Click(object sender, EventArgs e) { } private void aFunctionThatJustForDelegate(string result) { MessageBox.Show(result); } } }
委托的用處:
委托的優點,是可以實現異步(BeginInvoke),還可以在某種需要同時調用多個同參數、返回值的情況下簡化代碼。
二、事件
基本用法:
1.定義委托。
public delegate void SomeKindOfDelegate(string result);
2.定義事件。
public event SomeKindOfDelegate aDelegate;
3.為事件添加響應函數。
process.Exited += new EventHandler(CmdProcess_Exited);
4.為事件規定觸發(調用)方式。(【也可以沒有觸發方式,直接invoke】)
解說:
C#里,每一種‘事件Event'大概都對應着其‘事件處理者EventHandler'。比如Process類的OutputDataReceived事件對應着DataReceivedEventHandler,對於非特異性的‘事件',比如PasswordChanged 這種,它們統一都對應着RoutedEventHandler或者EventHandler這種較為通用的‘事件處理者'。然而,‘EventHandler'也只是充當了一個中介的角色,真正觸發了‘Event'之后要做什么,還需要我們手動指定,像這樣:
process.Exited += new EventHandler(CmdProcess_Exited); // 注冊進程結束事件 。
EventHandler本來也是委托。比如
public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e);
自定義事件
自定義事件是一種類似於委托的方式,
自定義事件某種意義上改變了程序的流程,使得某個條件的變化由‘不斷查詢'轉變為‘訂閱與處理'的關系。
自定義事件需要有以下幾個元素:
事件的發起者,事件的訂閱,以及事件的處理程序。從發起者到處理程序之間是可以傳參數的。
一個事件的‘發起'可以依賴於某種系統消息,比如‘OnKeyDown'、‘OnMouseClick'(【我目前還沒見過這么寫的源碼】),也可以在某個條件達成時(比如兩次輸入了同樣的字符)自行調用(其實收到系統消息也算是‘條件達成')。【更多的event是這么寫出來的】
有些事件,並沒有明顯的‘發起者'。
委托和事件是啥關系
委托和自定義事件的使用方式十分十分類似。event 只有類的內部可以 Invoke,delegate的話,在哪都可以Invoke。調用的方式貌似也略有區別(傳參方式)
由於調用方式和傳參的差異,event顯得更保守/穩定一些。event也更容易從‘理解'上更容易接受。
delegate貌似更多的用於進行異步(begin invoke)。而event則更多的用來做自定義事件。
委托和異步是啥關系
異步是委托可以實現的一種功能(或者叫做‘現象'也可以) 異步可以由很多種其他方式體現,比如多線程(thread,threadpool,task等等)。
多線程
.Net的委托本質上就是指向函數的指針,只不過這種指針是經過封裝后類型安全的。委托和線程是兩個不同的概念,線程是動態的,委托就是一個或一組內存地址,是靜態的。線程執行時如果遇到了指向函數的指針就執行這個函數。
.Net為了方便編程,給委托賦予了兩種方式以供調用線程來執行,即同步和異步方式,它們分別通過Invoke和BeginInvoke來開啟。Invoke就是同步執行,由調用線程來執行,而BeginInvoke則開啟了一個后台線程來執行delegate所指向的函數,這個后台線程和調用線程之間屬於異步執行方式。實際上有了delegate這個概念,你在編程時就可以不用直接使用Thread類來開辟新的線程了,因為微軟替你實現了。
使用BeginInvoke調用委托方法,其結果和調用一個新線程一樣。多線程編程在開發中經常用到,比如將后台計算和UI更新主線程分離,防止界面卡頓等,着重關注線程池ThreadPool,因為Task任務就是整理了它。
Task
.NET 4包含新名稱空間System.Threading.Tasks,它 包含的類抽象出了線程功能。Task 在后台使用ThreadPool。 任務表示應完成的某個單元的工作。 這個單元的工作可以在單獨的線程中運行,也可以以同步方式啟動一個任務,這需要等待主調線程。 使用任務不僅可以獲得一個抽象層,還可以對底層線程進行很多控制。
在安排需要完成的工作時,任務提供了非常大的靈活性。 例如,可以定義連續的工作—— 在一個任務完成后該執行什么工作。 這可以區分任務成功與否。 另外,還可以在層次結構中安排任務。例如,父任務可以創建新的子任務。 這可以創建一種依賴關系,這樣,取消父任務,也會取消其子任務。
啟動一個Task
要啟動任務,可 以使用 TaskFactory類 或 Task類 的構造函數和 Start()方法。Task類的構造函數在創建任務上提供的靈活性較大。
在啟動任務時,會創建Task類 的一個實例,利用Action或Action<object>委托不帶參數或帶一個object參數 ,可以指定應運行的代碼,這類似於Thread類 。下面定義了一個無參數的方法。 在實現代碼中,把任務的ID寫入控制台中:
static void TaskMethod() { Console.WriteLine("running in a task"); Console.WriteLine("Task id: {0}",Task.CurrentId); }
在上面的代碼中,可 以看到啟動新任務的不同方式。第一種方式 使用實例化TaskFactory類 ,在其中把 TaskMedlod()方 法傳遞給StartNew()方法,就會立即啟動任務。 第二種方式使用 Task類的構造函數。 實例化 Task對象時,任務不會立即運行,而是指定 Created狀態。接着調用 Task類的Start()方法,來啟動任務。 使用Task類 時,除了調用 Start()方法,還可以調用RunSynchronously()方法。這樣,任務也會啟動,但在調用者的當前線程中它正在運行,調用者需要一直等待到該任務結束。 默認情況下,任務是異步運行的。
//using task factory TaskFactory tf = new TaskFactory(); Task t1 = tf.StartNew(TaskMethod); //using the task factory via a task Task t2 = Task.TaskFactory.StartNew(TaskMethod); //using Task constructor Task t3 = new Task(TaskMethod); t3.Start();
使用Task類的構造函數和TaskFactory類的StartNew()方法時,都可以傳遞TaskCreationOptions枚舉中的值。設置LongRunning選項,可以通知任務調度器,該任務需要較長時間執行,這樣調度器更可能使用新線程。如果該任務應關聯到父任務上,而父任務取消了,則該任務也應取消,此時應設置 AuachToParent選項。PreferFairness的值表示,調度器應提取出已在等待的第一個任務。 如果一個任務在另一個任務內部創建,這就不是默認情況 。如果任務使用子任務創建了其他工作,子任務就優先於其他任務。 它們不會排在線程池隊列中的最后。 如果這些任務應以公平的方式與所有其他任務一起處理,就設置該選項為PreferFairness。
Task t4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness); t4.Start();
Action,Func等委托
泛型無返回值委托Action,有返回值Func,雖然使用Delegete委托可以達到同樣的效果,但是用Action等泛型委托寫法要更簡潔。除了Delegate委托我們還可以使用Action<T>和Func<T>委托。
泛型Action<T>委托表示引用一個void返回類型的方法。Action<T>委托類存在不同的變體,可以傳遞至多16種不同的參數類型,沒有泛型參數的Action類可以調用沒有參數的方法。例如:Action<in T1>調用帶一個參數的方法,Action<in T1,in T2>調用帶兩個參數的方法等
Func<T>的用法和Action<T>用法類似,但是Func<T>表示引用一個帶返回類型的方法,Func<T>也存在不同的變體,至多可以傳遞16個參數類型和1個返回類型,例如:Func<in T1,out Resout>表示帶一個參數的方法,Func<in T1,in T2,out Resout>表示調用帶兩個參數的方法。
下面就直接給一個Action<T>和Func<T>的例子
using System; namespace DelegateFuncAction { class Program { static void Main(string[] args) { Func<double, double,double> DoAddtion = calculate.addtion; double result = DoAddtion(20, 30); Console.WriteLine("Func帶返回參數委托做加法結果為:{0}",DoAddtion(10,20)); calculate c=new calculate(); Action<double, double> DoSubstraction = c.substraction; DoSubstraction(90, 20); } } class calculate { public static double addtion(double x, double y) { return x + y; } public void substraction(double x, double y) { Console.WriteLine("Action不帶返回參數委托做減法結果為:{0}",x-y); } } }