委託是C#中比較重要的概念,學習C#在這裏最容易產生迷惑,理解事後對後面的學習頗有幫助。
有些時候,因爲咱們在開發程序時對後續可能出現的要求及變化考慮不足而致使麻煩,這些新變化可能致使程序的從新編寫,那能不能改變這種狀況?後面的需求變化了,後續對應功能的編寫對前面的程序不形成影響?
能夠的,在C#中能夠用委託來解決這個問題。
舉個簡單的例子。
好比一個數據表須要導出,咱們在開始只是設計了導出到TXT和EXCEL,程序以下:
//定義類及使用方法
class HrDataInfo
{
//定義委託
public delegate void SaveAsDelegate(string FileName);
public SaveAsDelegate SaveAs;
public void SaveAsTxt(string FileName)
{
Console.WriteLine("將數據導出到TXT文件!{0}",FileName);
}
public void SaveAsExcel(string FileName)
{
Console.WriteLine("將數據導出到EXCEL文件!{0}",FileName);
}
}
上面定義了一個HrDataInfo類,類中有兩個方法,一個是SaveAsTxt(string FileName),一個是SaveAsExcel(string FileName),如何讓用戶來決定使用哪個呢?
玄機就在於上面的兩行代碼:
public delegate void SaveAsDelegate(string FileName);
public SaveAsDelegate SaveAs;
定義一個委託,先保證和定義方法同樣,等於定義一個統一的方法模子SaveAsDelegate(string FileName),委託實質上是類,由於不是靜態的因此要實例化爲SaveAs。
//使用
class Program
{
static void Main(string[] args)
{
HrDataInfo Hr = new HrDataInfo();
//輸出到Excel文件
Hr.SaveAs = Hr.SaveAsExcel;
Hr.SaveAs(@"C:\1.xls");
//輸出到TXT文件
Hr.SaveAs = Hr.SaveAsTxt;
Hr.SaveAs(@"C:\1.TXT");
//輸出到Word文件,是靜態方法,直接使用
Hr.SaveAs = SaveAsWord;
Hr.SaveAs(@"C:\1.Word");
//輸出到PPT文件,非靜態,實例化對象後引用
OthersDealWith ODW=new OthersDealWith ();
Hr.SaveAs =ODW.SaveAsPPT;
Hr.SaveAs(@"C:\1.PPT");
Console.ReadKey();
}
static void SaveAsWord(string FileName)
{
Console.WriteLine("將數據導出到Word文件!{0}",FileName);
}
}
public class OthersDealWith
{
public void SaveAsPPT(string FileName)
{
Console.WriteLine("將數據導出到PPT文件!{0}",FileName);
}
}
由於原來的類中只有導出到Excel和TXT,如今須要導出到Word和PPT,而原來的類中沒有這個方法,怎麼辦?
寫上相應的處理方法,而後賦值給委託就好了。
//另外的寫法
Hr.SaveAs = new HrDataInfo.SaveAsDelegate(Hr.SaveAsExcel);
Hr.SaveAs(@"C:\1.xls");
上面也是對的,是通常的寫法。
總結:
1、委託實際上就是指針,就是方法的地址,程序中你讓它指向哪一個方法它就指向哪一個方法;
2、委託是統一的方法模型,參數必須一致;
3、委託其實是把方法看成參數來傳遞,能夠是靜態方法也能夠是非靜態的方法。
從上面的例子中看出,類中定義了委託給程序帶來了很大的靈活性,有一個類放在那裏,裏面藏了個指針,你讓它指向哪裏它就指向哪裏(固然有約定)。這讓咱們想到了事件,好比一個按鈕放在窗體上,若是裏面也藏了這麼個東西,是否是就能夠處理相應的點擊,好比,你點擊了按鈕,我讓它指向一個處理程序,那麼這個是否是就有了所謂的按鈕響應,其實,這就是事件,叫按鈕的點擊事件。
C#中是否是這樣處理的呢?
新鍵一個窗體程序,隨便放一個按鈕button1到窗體form1上,在咱們沒有爲按鈕寫處理程序(鼠標點擊)以前,看Form1.Designer.cs中的代碼是這樣的:
this.button1.Location = new System.Drawing.Point(29, 51);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
如今,咱們雙擊按鈕button1,爲它編寫鼠標點擊事件,看看系統爲咱們作了什麼?
第一個變化,系統自動加了以下的代碼:
private void button1_Click(object sender, EventArgs e)
{
}
第二個變化,仍是看Form1.Designer.cs,
this.button1.Location = new System.Drawing.Point(29, 51);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
對比上面的代碼,多了
this.button1.Click += new System.EventHandler(this.button1_Click);
(+=,-=,+,-,是委託的運算,+和+=是訂閱,-和-=是退訂)
原來就是這樣的:this.button1.Click就是個委託,只是系統自動地把它指向了button1_Click(object sender, EventArgs e),因此,用戶在按鈕Button上點鼠標系統就自動運行button1_Click(object sender, EventArgs e)裏面的程序。
也能夠改動這些系統自動生成的代碼(通常建議不要動)。
好比,寫以下程序:
private void MineBT1_Click(object sender, EventArgs e)
{
MessageBox.Show("你點擊了按鈕!");
}
而後改動Form1.Designer.cs中的代碼 把 this.button1.Click += new System.EventHandler(this.button1_Click); 替換爲 this.button1.Click+=this.MineBT1_Click; 運行程序,同樣獲得的結果。 另外,在WPF程序中,還能夠經過屬性賦值的方式來把事件處理程序與事件的擁有者聯繫在一塊兒,好比你在窗體上放置一個按鈕,只要你爲這個按鈕的單擊事件寫了響應程序,系統自動爲你把兩者結合起來[綁定],就是屬性賦值。 Click="button1_Click" 固然你也能夠在程序中刪除它,經過C#語言來處理。 在類中寫下: this.button1.Click+=new RouteDEventHandler(button1_Click); 效果和屬性賦值是同樣的。 這自定義控件中,如何定義事件響應,好比用戶作了個自定義控件,裏面有個按鈕,它想讓用戶點擊這個按鈕時運行用戶所寫的代碼,由於控件已經封裝好了,裏面定義好按鈕的事件,在控件裏面或者外面寫處理方法,按照上面的模式處理就OK了。