今天要介紹的是C#的委派 Delegate
Delegate的發展由函數指標(funtion pointer)而來,可以說Delegate是函數指標中的語法糖(Syntactic sugar)也不為過,在Java中則是Sam類。早期在C# 1.0的時候,匿名函數與Lambda表達式還沒有被發展出來,C與C++有函數指標來負責打包函數(function),那C#呢?我們都知道物件導向程式OOP中所有的東西都該是物件,所以C#就創造了一個類別專門來打包函數。
本篇開始會依序從C#的發展順序介紹委派
Delegate、Anonymous Function、Lambda Expression、Func<>、Action<>
委派含括很大的範圍,故可能會花上幾篇的篇幅來一一介紹。
1.Delegate
首先來介紹的是最初的委派(Delegate)。月亮是外國的圓,我們先來看看微軟(MicroSoft)怎麼介紹自家的的委派--A delegate is a reference type that can be used to encapsulate a named or an anonymous method--一種用來封裝具名或匿名方法的參考類型。至於為什麼封裝函數叫做委派呢,不如試想,原本有一個流程方法(Sop)是要由A公司去完成的,但我們將他外包給委派給B公司去做,把方法流程外包的行為就叫做委派,這樣不難理解吧。
廢話不多說,立刻來看委派是如何被宣告(declare)以及使用的。
委派的宣告:
public delegate int MyDelegete(int x);
我們可以注意到一個委派類型的宣告簽章(Signatures),就像是類似宣告一個方法般,給他回傳型態、名稱、參數就完成宣告了,所以我們先做的是宣告一個回傳型態為int、委派類型名稱為Mydelegate、參數型態為int的委派宣告。好啦,宣告完成拉接下來是封裝方法。既然委派是一種類型,所以我們必須將他實作(implement)。
挖哩怎麼報紅了,原來委派類型變數的宣告必須在建構子參數中給與這個符合此委派類型簽章的方法,於是我們必須相應而生出一個回傳型態、參數型態皆為int的方法來完成委派變數的宣告。
好吧,事情發展至此,但或許我們這時候產生了一個疑惑,這樣的用法,委派看起來是不是跟方法沒什麼差別呢,那我們幹嘛使用委派呢,我們必須再重新思考為何使用委派,為何要把方法封裝進變數內。
假如今天有一個十個流程(方法、函數),這十個流程如果大部分都在做一樣的事,但其中只有小部分的不同呢?是不是就可以把這小部分不同邏輯的地方抽出來呢?
我們可以發現--再相似的流程中把不同的邏輯抽出來就是委派的精隨。
事實上,委派能做的事遠遠不只如此,應用很多,但核心理念是不變的--就是我們將方法邏輯外包封裝至委派變數中,要怎麼使用端看大家囉。
挖哩怎麼報紅了,原來委派類型變數的宣告必須在建構子參數中給與這個符合此委派類型簽章的方法,於是我們必須相應而生出一個回傳型態、參數型態皆為int的方法來完成委派變數的宣告。
public delegate int MyDelegete(int x);
static void Main(string[] args)
{
MyDelegete myDelegete = new MyDelegete(AddOne);
}
public static int AddOne(int x)
{
return ++x;
}
我們對於委派變數的宣告終於成功了,馬上來使用他吧 static void Main(string[] args)
{
int i = 1;
MyDelegete myDelegete = new MyDelegete(AddOne);
//MyDelegete myDelegete = AddOne; //或是在C# 2.0 中可以這樣宣告
i = myDelegete(i);
Console.WriteLine(i); //Output 2
Console.ReadKey();
}
好吧,事情發展至此,但或許我們這時候產生了一個疑惑,這樣的用法,委派看起來是不是跟方法沒什麼差別呢,那我們幹嘛使用委派呢,我們必須再重新思考為何使用委派,為何要把方法封裝進變數內。
假如今天有一個十個流程(方法、函數),這十個流程如果大部分都在做一樣的事,但其中只有小部分的不同呢?是不是就可以把這小部分不同邏輯的地方抽出來呢?
我們可以發現--再相似的流程中把不同的邏輯抽出來就是委派的精隨。
static void Main(string[] args)
{
var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine(string.Join(",", Change1(array)));
Console.WriteLine(string.Join(",", Change2(array)));
Console.WriteLine(string.Join(",", Change3(array)));
Console.ReadKey();
//2,3,4,5,6,7,8,9,10
//2,4,6,8,10,12,14,16,18
//1,4,9,16,25,36,49,64,81
}
public static int[] Change1(int[] _array)
{
var array = new int[_array.Length];
for (int i = 0, c = _array.Length; i < c; i++)
{
array[i] = _array[i] + 1;
}
return array;
}
public static int[] Change2(int[] _array)
{
var array = new int[_array.Length];
for (int i = 0, c = _array.Length; i < c; i++)
{
array[i] = _array[i] * 2;
}
return array;
}
public static int[] Change3(int[] _array)
{
var array = new int[_array.Length];
for (int i = 0, c = _array.Length; i < c; i++)
{
array[i] = _array[i] * _array[i];
}
return array;
}
觀察以上的方法,發現這三個方法大部分的邏輯都很像,唯獨內部對Array各個元素處理的方式不一樣,試著用新學到的委派來抽出不同的邏輯吧。 static void Main(string[] args)
{
var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
MyDelegete myDelegete1 = new MyDelegete(AddOne);
MyDelegete myDelegete2 = new MyDelegete(MultipleTwo);
MyDelegete myDelegete3 = new MyDelegete(Square);
Console.WriteLine(string.Join(",", Change(array, myDelegete1)));
Console.WriteLine(string.Join(",", Change(array, myDelegete2)));
Console.WriteLine(string.Join(",", Change(array, myDelegete3)));
Console.ReadKey();
//2,3,4,5,6,7,8,9,10
//2,4,6,8,10,12,14,16,18
//1,4,9,16,25,36,49,64,81
}
public static int AddOne(int number)
{
return ++number;
}
public static int MultipleTwo(int number)
{
return number*2;
}
public static int Square(int number)
{
return number * number;
}
public static int[] Change(int[] _array, MyDelegete myDelegete)
{
var array = new int[_array.Length];
for (int i = 0, c = _array.Length; i < c; i++)
{
array[i] = myDelegete(_array[i]);
}
return array;
}
恩,我們可能會疑惑,看起來沒減少很多程式碼阿,但又隨即可以發現,假如Change方法是一百行呢? 三遍不就是三百行了呢?又或是有一個流程已經先設定好大部分的邏輯,而有小部分希望使用者自訂義邏輯使的程式使用更彈性呢?事實上,委派能做的事遠遠不只如此,應用很多,但核心理念是不變的--就是我們將方法邏輯外包封裝至委派變數中,要怎麼使用端看大家囉。
最後做個重點總結:
- 委派(delegate)是一種參考類型(reference type),負責具名或匿名方法的封裝。
- 委派的宣告類似於方法(function),只有與委派相同簽章(Signatures)的方法能被封裝。
- 委派物件變數宣告的同時才將方法封裝,而變數就是被封裝的方法。
小小心得
若有謬誤
歡迎底下留言指正
下一篇:C# 委派(Delegate) (二) - Anonymous Function、Lambda Expression
參考:
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/
若有謬誤
歡迎底下留言指正
下一篇:C# 委派(Delegate) (二) - Anonymous Function、Lambda Expression
參考:
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/
我看了好幾篇委派的文章,您寫的最為淺顯易懂,十分感謝
回覆刪除寫得很好
回覆刪除