(筆記) Delegate的再進化:Action Generic Delegate (.NET) (C#)

Abstract
C# 1.0提出了Delegate取代C/C++的function pointer後,C# 2.0提出了Anonymous methods,C# 3.0提出了Action Generic Delegate與Lambda expressions,我們來看看這些新的技術如何取代function pointer?

Introduction
使用環境:Visual Studio 2008 + .NET Framework 3.5 + C# 3.0

C的Function Pointer

在C的時代,若要將function當成參數傳到另外一個function,靠的就是function pointer這個必殺技。

function_pointer.c / C

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : function_pointer.v
5 Compiler : Visual Studio 2008
6 Description : function pointer by C
7 Release : 02/12/2011 1.0
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12
13 typedef void (*fptr)(char *s); // function pointer
14  void write_to_console(char *s);
15  void write_to_file(char *s);
16  void display_message(fptr m, char *s);
17
18  int main() {
19 int i;
20 char s[] = "Hello World";
21 fptr m;
22
23 printf("1:write to console, 2:write to text\n");
24 i = getchar();
25
26 if (i == 49) // ASCII : 1
27   m = write_to_console;
28 else
29 m = write_to_file;
30
31 display_message(m, s); // use function pointer to pass a function
32  }
33
34  void write_to_console(char *s) {
35 printf("%s\n", s);
36 }
37
38  void write_to_file(char *s) {
39 FILE *fp;
40 fp = fopen("test.txt", "w");
41 fwrite(s, strlen(s), 1, fp);
42 fclose(fp);
43 }
44
45  void display_message(fptr m, char *s) {
46 (*m)(s); // use function pointer to call function
47  }

13行

typedef void (*fptr)(char *s); // function pointer

定義一個function pointer型別fptr,該function為回傳void,傳入為char *。

21行

fptr m;

使用剛定義的ftpr型別定義一個function pointer m。

27行

m = write_to_console;

將一個function指定給該function pointer。

31行

display_message(m, s); // use function pointer to pass a function

將該function pointer傳進display_message()這個function,在此我們首次將function變成參數,透過function pointer的方式傳進一個function,這也是function pointer的主要功能。

46行

(*m)(s); // use function pointer to call function

m為一個function pointer,故需要使用(*m)變成一個function使用,之後再傳入該function的參數。

C# 1.0的Delegate

C#是個OOP,所以將function pointer變成Delegate Object,以前在C是要傳遞pointer,現在在C#是要傳遞object。

use_delegate.cs / C#

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : use_delegate.cs
5 Compiler : Visual Studio 2008
6 Description : delegate by C# 1.0
7 Release : 02/12/2011 1.0
8  */
9
10  using System;
11  using System.IO;
12
13  delegate void display_method(string s); // declare delegate class type
14  
15  class use_delegate {
16 static void Main(string[] args) {
17 Console.WriteLine("1:write to console, 2:write to text");
18 int i = Console.Read();
19 string s = "Hello World";
20
21 display_method m; // instantiate delegate object m
22   if (i == 49) // ACSII 1
23   m = write_to_console; // assign method to delegate object
24   else
25 m = write_to_file;
26
27 display_message(m, s); // use delegate to pass a method
28   }
29
30 static void write_to_console(string s) {
31 Console.WriteLine(s);
32 }
33
34 static void write_to_file(string s) {
35 using (StreamWriter sw = new StreamWriter("test.txt"))
36 sw.WriteLine(s);
37 }
38
39 static void display_message(display_method m, string s) {
40 m(s); // use delegate to call method
41   }
42 }

13行

delegate void display_method(string s); // declare delegate class type

定義一個delegate型別display_method,該delegate為回傳void,傳入char *。

21行

display_method m; // instantiate delegate object m

使用剛定義的display_method delegate建立一個m delegate object。

23行

m = write_to_console; // assign method to delegate object

將1個method指定給該delegate object。

27行

display_message(m, s); // use delegate to pass a method

將該delegate object傳進display_message()這個method,在此我們首次看到C# 1.0利用delegate將method變成參數,透過delegate object的方式傳進一個method。

40行

m(s); // use delegate to call method

m已經是一個object,故可以直接傳入參數。

在此我們可以看到C# 1.0的delegate成功地隱藏了function pointer的那顆pointer,C最難學的就是pointer,後來的語言幾乎都使用compiler的力量,用各種syntax sugar去隱藏pointer,C#也不例外,其實C這段程式碼幾乎與C#完全相同,只是將function pointer改成delegate而已。

C# 2.0的Anomymous Method

anonymous_method.cs / C#

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : anonymous_method.cs
5 Compiler : Visual Studio 2008
6 Description : anonymous method by C# 2.0
7 Release : 02/12/2011 1.0
8  */
9
10  using System;
11  using System.IO;
12
13  delegate void display_method(string s); // declare delegate class type
14  
15  class anonymous_method {
16 static void Main(string[] args) {
17 Console.WriteLine("1:write to console, 2:write to text");
18 int i = Console.Read();
19 string s = "Hello World";
20
21 display_method m; // instantiate delegate object m
22  
23 if (i == 49)
24 m = delegate(string t) { // anonymous method
25   Console.WriteLine(t);
26 };
27 else
28 m = delegate(string t) { // anomymous method
29   using (StreamWriter sw = new StreamWriter("test.txt"))
30 sw.WriteLine(t);
31 };
32
33 display_message(m, s);
34 }
35
36 static void display_message(display_method m, string s) {
37 m(s);
38 }
39 }

24行

m = delegate(string t) { // anonymous method
Console.WriteLine(t);
};

使用了anonymous method定義要指定給delegate的method實質內容,其實anonymous method已經有lambda的味道,主要是在解決以往OOP中,總是要為一些只有一兩行code的東西另開一個method的困擾,這在C++使用STL時更可以明顯的發現這種困擾,C# 2.0在此整個架構仍使用delegate,只是加上anonymous method讓整體更精簡。

C# 3.0的Action<T> Generic Delegate與Lambda

use_lambda.cs / C#

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : use_lambda.cs
5 Compiler : Visual Studio 2008
6 Description : function pointer by C# 3.0
7 Release : 02/12/2011 1.0
8  */
9
10 using System;
11 using System.IO;
12
13 class use_lambda {
14 static void Main(string[] args) {
15 Console.WriteLine("1:write to console, 2:write to text");
16 int i = Console.Read();
17 string s = "Hello World";
18
19 Action<string> m; // Action<T> generic delegate
20
21 if (i == 49)
22 m = t => Console.WriteLine(t); // lambda
23 else
24 m = t => { // lambda
25 using (StreamWriter sw = new StreamWriter("test.txt"))
26 sw.WriteLine(t);
27 };
28
29 display_message(m, s);
30 }
31
32 static void display_message(Action<string> m, string s) {
33 m(s);
34 }
35 }

19行

Action<string> m; // Action<T> generic delegate

C# 3.0提出了Action<T>,簡單的說就是將delegate也泛型化,讓你不用在自己宣告delegate type,直接要用就可以用,另外還有Func<TResult>,有興趣請參考MSDN Func<TResult> Delegate

22行

m = t => Console.WriteLine(t); // lambda

C# 3.0終於提出了Lambda取代了Anonymous Method,我自己其實還蠻喜歡C# 3.0的Lambda,綜觀各種語言的Lambda,就屬C# 3.0的Lambda最帥,最乾淨俐落。

在C# 3.0已經看不到delegate,而是用Action<T>與Func<TResult>這種泛型的delegate取代,另外加上Lambda之後,讓整個Delegate更為完整。

完整程式碼下載
function_pointer.7z (使用C的function pointer)
use_delegate.7z (使用C# 1.0的delegate)
anonymous_method.7z (使用C# 2.0的anonymous method)
use_lambda.7z (使用C# 3.0的Action<T>與Lambda)

Conclusion
本文使用了MSDN Action<T> Delegate所提出的範例加以修改,並加上C的function pointer版本做比對,作為學習Action<T> Generic Delegate的一個紀錄。

C++的function pointer解決方案為Function Object,請參考(原創) Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (C#)

目前看起來,C、C++、C#的解決方案中,屬C# 3.0的Action<T>與Lambda最好,最簡潔也最直觀。

使用過這麼多語言,只有兩個語言是真的讓我打從心裡喜歡,一個是C語言,一個是C#。

喜歡C語言的原因是它是個小語言,也因為小,所以在各個平台都有完整實作,是一個真正的跨平台語言,而且它很少有Compiler動手腳,可以讓你完整掌握,雖然pointer讓我們又愛又恨,有很高的學習門檻,但只要搞清楚,C是一個很好掌握的語言。

喜歡C#語言的原因是他語法實在乾淨漂亮,一整個看起來就是舒服,不像Java那樣囉唆,也不像C++那樣繁瑣,寫起程式的感覺如行雲流水,非常爽快。

Reference
MSDN Action<T> Delegate
MSDN Func<TResult> Delegate

See Also

(原創) Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (C#)

全文完。

posted on 2011-02-08 23:18 真 OO无双 阅读(...) 评论(...) 编辑 收藏

导航

公告