1、C#委托是什么
c#中的委托可以理解一種類,這種類實例化后可以將函數的包裝成一個變量(該變量就變成了對該函數的“引用”),它使得這個變量(函數)可以作為參數來被傳遞,這在作用上相當於c中的函數指針。c用函數指針獲取函數的入口地址,然后通過這個指針來實現對函數的操作。
委托的定義和方法的定義類似,只是在定義的前面多了一個delegate關鍵字。如下定義:
public delegate void MyDelegate(int para1, string para2);
委托能包裝的方法是有一定限制的,例如能被前面的委托類型MyDelegate包裝的方法需要滿足以下條件:
1.方法的返回類型必須為void;
2.方法必須有兩個參數,並且第一個參數應為int類型,第二個參數為string類型。
總結:可以被委托包裝的方法必須滿足以下規則:
1.方法的簽名必須與委托一致,方法簽名包括參數的個數、類型和順序;
2.方法的返回類型要和委托一致,注意,方法的返回類型不屬於方法簽名的一部分。

2、委托的使用
//委托使用的演示
class Program
{
public delegate void MyDelegate(int para1, int para2); //1.使用delegate關鍵字來定義一個委托類型
static void Main(string[] args)
{
MyDelegate d; //2.聲明委托變量d
d = new MyDelegate(new Program().Add); //3.實例化委托類型,傳遞的方法也可以為靜態方法,這里傳遞的是實例方法
MyMethod(d); //4.委托類型作為參數傳遞給另一個方法
Console.ReadKey();
}
void Add(int para1, int para2)
{
int sum = para1 + para2;
Console.WriteLine("兩個數的和為:" + sum);
}
private static void MyMethod(MyDelegate mydelegate)
{
mydelegate(1, 2); //5.在方法中調用委托
}
}
從以上代碼可以看出,使用委托的步驟為:定義委托類型—聲明委托變量—實例化委托—將實例作為參數傳遞給另一個方法—該方法調用委托。如下具體分析委托的使用過程。
(1)定義委托類型: public delegate void MyDelegate(int a, int b);。其定義方式類似於方法的定義,只是多了一個delegate關鍵字。
(2)聲明委托變量:MyDelegate d;既然委托是一種類型,那么可以使用委托來聲明一個委托變量,相當於int a;
(3)實例化委托: d = new MyDelegate(new Program().Add);。第二步只是聲明了委托變量,但並沒有將它實例化。類的實例化使用new關鍵字來實現,而委托也屬於一種“類”,所以委托的實例化也使用new關鍵字來進行的。這里需要注意的是,委托的實例化是用一個方法名(不能帶左右括號)作為參數,並且該方法的定義必須符合委托的定義,即該方法的返回類型、參數個數和類型必須與委托定義中的一樣。這樣,前面3步就好比構造了一個律師對象,而方法InstanceMethod好比是當事人的方法。
(4)作為參數傳遞給方法:MyMethod(d);。委托使用得在C#中,可以把一個方法作為另一個方法的參數,而委托可以看作是一個包裝方法的對象。
(5)在方法中調用委托。MyMethod(d);。委托使用得在c#中,可以把一個方法作為另一個方法的參數,而委托可以看作是一個包裝方法的對象。
總結:在使用委托時,需要注意以下幾個問題。
1.在第三步中,被傳遞的方法的定義必須與委托定義相同,即方法的返回類型和參數個數、參數類型都必須與委托相同。並且,傳遞的是方法名,方法名后不能帶有左右括號。
2.在第五步中,委托的調用與方法調用類似,傳遞的實參類型和個數必須與委托定義一致。
3.由於委托是方法的包裝類型,所以對委托的調用也就是對其所包裝的的方法的調用,上面第5步時間上是調用了Add方法來對傳入的實參進行計算。
3、委托鏈的使用
委托鏈其實就是委托類型,只是委托鏈把多個委托鏈接在一起而已,也就是說,我們把鏈接了多個方法的委托稱為委托鏈或多路廣播委托。如下:
public delegate void DelegateTest();
static void Main(string[] args)
{
//用靜態方法來實例化委托
DelegateTest dtstatic = new DelegateTest(Program.method1);
DelegateTest dtinstance = new DelegateTest(new Program().method2);
DelegateTest delegatechain = null; //定義一個委托對象,一開始初始化為null,即不代表任何方法。
//使用 “+”符號鏈接委托,鏈接多個委托后就成為了委托鏈
delegatechain += dtstatic;
delegatechain += dtinstance;
//調用委托鏈
delegatechain(); // 輸出兩行"這是靜態方法"和"這是實例方法"
Console.Read();
}
private static void method1()
{
Console.WriteLine("這是靜態方法");
}
private void method2()
{
Console.WriteLine("這是實例方法");
}
從委托鏈中移除委托
//使用 “+”符號鏈接委托,鏈接多個委托后就成為了委托鏈
delegatechain += dtstatic;
delegatechain += dtinstance;
//使用 “-”運算符 移除委托
delegatechain -= dtstatic;
4、什么要使用委托
上一章中我們可能會很疑惑,為什么需要委托?為什么不直接在MyMethod方法里直接調用Add方法,反而要實例化一個委托對象來完成調用呢?這豈不是自找麻煩嗎?
當然,c#引入委托並不是自找麻煩。委托是c#最好的一個特性,它為后來的很多特性都打下了基礎。委托使得一個方法可以作為另一個方法的參數進行傳遞,這就是委托最大的作用。如下例子:
例如我們要實現一個打招呼的方法,而每個國家打招呼的方式都不一樣,剛開始我們可能會像下面這樣實現打招呼的方法:
public void Greeting(string name, string language)
{
switch (language)
{
case "zh-cn":
ChineseGreeting(name);
break;
case "en-us":
EnglishGreeting(name);
break;
default:
EnglishGreeting(name);
break;
}
}
public void EnglishGreeting(string name)
{
Console.WriteLine("Hello, " + name);
}
public void ChineseGreeting(string name)
{
Console.WriteLine("你好, " + name);
}
若后續我們需要添加德國、日本等打招呼方法,就必須修改Greeting方法內的case語句,來適應新的需求,這樣特別不方便。有了委托,我們就可以把函數作為參數,並像如下代碼實現Greeting方法:
public delegate void GreetingDelegate(string name);
static void Main(string[] args)
{
//引入委托
Program p = new Program();
p.Greeting("小葉", p.ChineseGreeting); // 將所使用的的方法ChineseGreeting作為參數傳遞
p.Greeting("Tommy Li", p.EnglishGreeting);
Console.Read();
}
public void Greeting(string name, GreetingDelegate callback)
{
callback(name); // 調用ChineseGreeting方法
}
public void EnglishGreeting(string name)
{
Console.WriteLine("Hello, " + name);
}
public void ChineseGreeting(string name)
{
Console.WriteLine("你好, " + name);
}
文章轉載自:【c# 學習筆記】c#委托是什么
5、例子
class Program
{
static void Main(string[] args)
{
Bookstore XinHua = new Bookstore(); // 實例化一個新化書店
Reader LiNing = new Reader(); // 實例化一個讀者李寧
// 以new關鍵字實例化一個委托綁定到onpublish事件
// 傳遞的方法可以是“靜態方法”,也可以是“實例方法”
XinHua.onpublish += new Bookstore.publish(LiNing.issue);
XinHua.issue();
Console.ReadKey();
}
}
public class Bookstore
{
public delegate void publish(); //聲明委托
public event publish onpublish; //聲明委托注冊的事件
//聲明觸發方法
public void issue()
{
Console.WriteLine("書店發布了一本雜志");
this.onpublish();
}
}
public class Reader
{
// 定義收到雜志的方法
public void issue()
{
Console.WriteLine("我收到了一本雜志");
}
}
運行結果:

示例2
class Program
{
static void Main(string[] args)
{
Bookstore XinHua = new Bookstore(); // 實例化一個新化書店
Reader LiNing = new Reader("李寧"); // 實例化一個讀者李寧,訂閱了電腦類雜志
Reader LiSi = new Reader("李四"); // 實例化一個讀者李四,訂閱了英語類雜志
// 李寧只訂閱電腦類雜志事件
XinHua.on_publish_computer += new Bookstore.publish_computer(LiNing.received_book);
// 李四兩種雜志都訂閱了
XinHua.on_publish_computer += new Bookstore.publish_computer(LiSi.received_book);
XinHua.on_publish_english += new Bookstore.publish_english(LiSi.received_book);
XinHua.issue_computer();
XinHua.issue_english();
Console.ReadKey();
}
}
public class Bookstore
{
// 委托有些類“似於”類的靜態方法, 可以通過Bookstore.publish_computer進行訪問
public delegate void publish_computer(string bookname); //聲明電腦的委托
public event publish_computer on_publish_computer; //聲明委托注冊的事件
//發行電腦的觸發方法
public void issue_computer()
{
Console.WriteLine("書店發布了一本電腦類雜志");
this.on_publish_computer("《電腦周報》");
}
public delegate void publish_english(string bookname); //聲明自己的委托
public event publish_english on_publish_english; //聲明委托注冊的事件
//發行電腦的觸發方法
public void issue_english()
{
Console.WriteLine("書店發布了一本英語類雜志");
this.on_publish_english("《英語周報》");
}
}
public class Reader
{
public string name;
public Reader(string n) { this.name = n; }
// 定義收到雜志的方法
public void received_book(string bookname)
{
Console.WriteLine(this.name + "收到了一本雜志" + bookname);
}
}
運行結果:

示例3
class Program
{
static void Main(string[] args)
{
Bookstore XinHua = new Bookstore("新華書店"); // 實例化一個新化書店
Reader LiNing = new Reader("李寧"); // 實例化一個讀者李寧,訂閱了電腦類雜志
Reader LiSi = new Reader("李四"); // 實例化一個讀者李四,訂閱了英語類雜志
// 李寧和李四訂閱電腦類雜志事件
XinHua.PubComputer += new Bookstore.PubComputerEventHandler(LiNing.received_book);
XinHua.PubComputer += new Bookstore.PubComputerEventHandler(LiSi.received_book);
// 李四訂閱了英語類雜志事件
XinHua.PubEnglish += new Bookstore.PubEnglishEventHandler(LiSi.received_book);
// 手動觸發執行發布雜志事件
XinHua.issueComputer("電腦周刊", Convert.ToDateTime("2019.10.29"));
XinHua.issueEnglish("英語周刊", Convert.ToDateTime("2018.09.29"));
Console.ReadKey();
}
}
// 發布事件類
public class PubEventArgs : EventArgs
{
public string bookName;
public DateTime bookTime;
//構造函數
public PubEventArgs(string name, DateTime time) { this.bookName = name; this.bookTime = time; }
}
public class Bookstore
{
public string name;
public Bookstore(string n) { this.name = n; }
// 委托有些類似於類的靜態方法, 可以通過Bookstore.PubComputerEventHandler進行訪問
// 委托實際上是一種類,可以使用new關鍵字實例化
// 實例化后,就是對函數包裝(引用),使得函數可以作為參數傳遞或賦值。
public delegate void PubComputerEventHandler(object sender, PubEventArgs e); //聲明電腦的委托
public event PubComputerEventHandler PubComputer; //聲明委托注冊的事件
//發行電腦的觸發方法
public void issueComputer(string bookname, DateTime booktime)
{
Console.WriteLine("書店發布了一本電腦類雜志");
this.PubComputer(this, new PubEventArgs(bookname, booktime));
}
public delegate void PubEnglishEventHandler(object sender, PubEventArgs e); //聲明自己的委托
public event PubEnglishEventHandler PubEnglish; //聲明委托注冊的事件
//發行電腦的觸發方法
public void issueEnglish(string bookname, DateTime booktime)
{
Console.WriteLine("書店發布了一本英語類雜志");
this.PubEnglish(this, new PubEventArgs(bookname, booktime));
}
}
public class Reader
{
public string name;
public Reader(string n) { this.name = n; }
// 定義收到雜志的方法,參數不是一個簡單的變量而是PubEventArgs實例
public void received_book(object sender, PubEventArgs e)
{
Bookstore bs = (Bookstore)sender;
Console.WriteLine(string.Format("{0}收到了{1}書店發布的《{2}》 發布時間:{3}", this.name, bs.name, e.bookName, e.bookTime));
}
}
允行結果:

