目錄
委托
聲明委托
實例化委托
委托的多播
委托的用途
事件
通過事件使用委托
聲明事件
泛型
泛型的特性
泛型方法
泛型的委托
匿名方法
編寫匿名方法的語法
委托
類似于指針,委托是存有對某個方法的引用的一種引用類型變量,引用可以在運行時被改變。特別用于實現事件和回調方法。
聲明委托
委托聲明決定了可由委托引用的方法。委托可以指向一個具有相同標簽的方法。例如,有一個委托:
public delegate int MyDelegate (string s);
上面的委托可以被用于任何一個帶有單一string類型的方法,并且返回一個int類型的值。
語法:
delegate <return type> <delegate-name> <parameter list>
實例化委托
一旦聲明了委托類型,委托對象必須使用 new 關鍵字來創建,且與一個特定的方法有關。當創建委托時,傳遞到 new 語句的參數就像方法調用一樣書寫,但是不帶有參數。例如:
public delegate void printString(string s);
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
實例:
using System;
?
delegate int NumberChanger(int n);
namespace DelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}
?public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}
?static void Main(string[] args){// 創建委托實例NumberChanger nc1 = new NumberChanger(AddNum);NumberChanger nc2 = new NumberChanger(MultNum);// 使用委托對象調用方法nc1(25);Console.WriteLine("Value of Num: {0}", getNum());nc2(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}
結果:
Value of Num: 35
Value of Num: 175
委托的多播
委托的對象可以使用“+”運算符進行合并,可以由一個合并的委托來調用組成它的兩個委托。運算符“-”可以將合并的委托移除出去。
使用委托的這個有用的特點,您可以創建一個委托被調用時要調用的方法的調用列表。這被稱為委托的 多播(multicasting),也叫組播。
using System;
?
delegate int NumberChanger(int n);
namespace DelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}
?public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}
?static void Main(string[] args){// 創建委托實例NumberChanger nc;NumberChanger nc1 = new NumberChanger(AddNum);NumberChanger nc2 = new NumberChanger(MultNum);nc = nc1;nc += nc2;// 調用多播nc(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}
結果:
Value of Num: 75
委托的用途
下面的實例演示了委托的用法。委托 printString 可用于引用帶有一個字符串作為輸入的方法,并不返回任何東西。
我們使用這個委托來調用兩個方法,第一個把字符串打印到控制臺,第二個把字符串打印到文件:
using System;
using System.IO;
?
namespace DelegateAppl
{class PrintString{static FileStream fs;static StreamWriter sw;// 委托聲明public delegate void printString(string s);
?// 該方法打印到控制臺public static void WriteToScreen(string str){Console.WriteLine("The String is: {0}", str);}// 該方法打印到文件public static void WriteToFile(string s){fs = new FileStream("e:\\message.txt", FileMode.Append, FileAccess.Write);sw = new StreamWriter(fs);sw.WriteLine(s);sw.Flush();sw.Close();fs.Close();}// 該方法把委托作為參數,并使用它調用方法public static void sendString(printString ps){ps("Hello World");}static void Main(string[] args){printString ps1 = new printString(WriteToScreen);printString ps2 = new printString(WriteToFile);sendString(ps1);sendString(ps2);Console.ReadKey();}}
}
結果:
控制臺:
The String is: Hello World
message.txt文件:
Hello World
事件
事件是在說一個用戶操作,比如按鍵、點擊、鼠標移動等,或者是一些提示信息,比如系統生成的通知,應用程序需要在事件發生時響應事件。使用事件機制實現線程間的通信。
通過事件使用委托
事件在類中聲明且生成,并且通過使用同一個類或者其他類中的委托與事件處理程序關聯。包含事件的類用于發布事,稱為發布器類。其他接受該事件的類被稱為訂閱器類。事件使用發布訂閱模型。發布器是一個包含事件和委托定義的對象。事件和委托之間的聯系也定義在這個對象中。發布器類的對象調用這個事件,并且通知其他對象。
訂閱器是一個介=接受事件并且提供事件處理程序的對象。在發布器類中的委托調用訂閱器類中的方法。
聲明事件
類的內部聲明事件,首先必須聲明該事件的委托類型。例如:
public delegate void BoilerLogHandler(string status);
然后,聲明事件本身,使用 event 關鍵字:
// 基于上面的委托定義事件
public event BoilerLogHandler BoilerEventLog;
上面的代碼定義了一個名為 BoilerLogHandler 的委托和一個名為 BoilerEventLog 的事件,該事件在生成的時候會調用委托。
using System;
namespace SimpleEvent
{using System;/***********發布器類***********/public class EventTest{private int value;
?public delegate void NumManipulationHandler();
?
?public event NumManipulationHandler ChangeNum;protected virtual void OnNumChanged(){if (ChangeNum != null){ChangeNum(); /* 事件被觸發 */}else{Console.WriteLine("event not fire");Console.ReadKey(); /* 回車繼續 */}}
?
?public EventTest(){int n = 5;SetValue(n);}
?
?public void SetValue(int n){if (value != n){value = n;OnNumChanged();}}}
?
?/***********訂閱器類***********/
?public class subscribEvent{public void printf(){Console.WriteLine("event fire");Console.ReadKey(); /* 回車繼續 */}}
?/***********觸發***********/public class MainClass{public static void Main(){EventTest e = new EventTest(); /* 實例化對象,第一次沒有觸發事件 */subscribEvent v = new subscribEvent(); /* 實例化對象 */e.ChangeNum += new EventTest.NumManipulationHandler(v.printf); e.SetValue(7);e.SetValue(11);}}
}
結果:(需要回車進行觸發事件)
event not fire
event fire
event fire
并且可以使用事件來講信息記錄到日志中:
using System;
using System.IO;
?
namespace BoilerEventAppl
{
?// boiler 類class Boiler{private int temp;private int pressure;public Boiler(int t, int p){temp = t;pressure = p;}
?public int getTemp(){return temp;}public int getPressure(){return pressure;}}// 事件發布器class DelegateBoilerEvent{public delegate void BoilerLogHandler(string status);
?// 基于上面的委托定義事件public event BoilerLogHandler BoilerEventLog;
?public void LogProcess(){string remarks = "O. K";Boiler b = new Boiler(100, 12);int t = b.getTemp();int p = b.getPressure();if (t > 150 || t < 80 || p < 12 || p > 15){remarks = "Need Maintenance";}OnBoilerEventLog("Logging Info:\n");OnBoilerEventLog("Temparature " + t + "\nPressure: " + p);OnBoilerEventLog("\nMessage: " + remarks);}
?protected void OnBoilerEventLog(string message){if (BoilerEventLog != null){BoilerEventLog(message);}}}// 該類保留寫入日志文件的條款class BoilerInfoLogger{FileStream fs;StreamWriter sw;public BoilerInfoLogger(string filename){fs = new FileStream(filename, FileMode.Append, FileAccess.Write);sw = new StreamWriter(fs);}public void Logger(string info){sw.WriteLine(info);}public void Close(){sw.Close();fs.Close();}}// 事件訂閱器public class RecordBoilerInfo{static void Logger(string info){Console.WriteLine(info);}//end of Logger
?static void Main(string[] args){BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt");DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();boilerEvent.BoilerEventLog += newDelegateBoilerEvent.BoilerLogHandler(Logger);boilerEvent.BoilerEventLog += newDelegateBoilerEvent.BoilerLogHandler(filelog.Logger);boilerEvent.LogProcess();Console.ReadLine();filelog.Close();}//end of main
?}//end of RecordBoilerInfo
}
結果,在目標文件中顯示日志為:
Logging Info:
?
Temparature 100
Pressure: 12
?
Message: O. K
泛型
泛型允許編寫一個可以在任何類型下一起工作的類和方法,可以通過數據類型的替代參數編寫類或方法的規范。當編譯器遇到類的構造函數或方法的函數調用時,它會生成代碼來處理指定的數據類型。
using System;
using System.Collections.Generic;
?
namespace GenericApplication
{public class MyGenericArray<T>{private T[] array;public MyGenericArray(int size){array = new T[size + 1];}public T getItem(int index){return array[index];}public void setItem(int index, T value){array[index] = value;}}class Tester{static void Main(string[] args){// 聲明一個整型數組MyGenericArray<int> intArray = new MyGenericArray<int>(5);// 設置值for (int c = 0; c < 5; c++){intArray.setItem(c, c*5);}// 獲取值for (int c = 0; c < 5; c++){Console.Write(intArray.getItem(c) + " ");}Console.WriteLine();// 聲明一個字符數組MyGenericArray<char> charArray = new MyGenericArray<char>(5);// 設置值for (int c = 0; c < 5; c++){charArray.setItem(c, (char)(c+97));}// 獲取值for (int c = 0; c < 5; c++){Console.Write(charArray.getItem(c) + " ");}Console.WriteLine();Console.ReadKey();}}
}
結果:
0 5 10 15 20
a b c d e
泛型的特性
使用泛型是一種增強程序功能的技術,具體表現在以下幾個方面:
-
它有助于您最大限度地重用代碼、保護類型的安全以及提高性能。
-
您可以創建泛型集合類。.NET 框架類庫在 System.Collections.Generic 命名空間中包含了一些新的泛型集合類。您可以使用這些泛型集合類來替代 System.Collections 中的集合類。
-
您可以創建自己的泛型接口、泛型類、泛型方法、泛型事件和泛型委托。
-
您可以對泛型類進行約束以訪問特定數據類型的方法。
-
關于泛型數據類型中使用的類型的信息可在運行時通過使用反射獲取。
泛型方法
我們可以通過類型參數聲明泛型方法,例如:
using System;
using System.Collections.Generic;
?
namespace GenericMethodAppl
{class Program{static void Swap<T>(ref T lhs, ref T rhs){T temp;temp = lhs;lhs = rhs;rhs = temp;}static void Main(string[] args){int a, b;char c, d;a = 10;b = 20;c = 'I';d = 'V';
?// 在交換之前顯示值Console.WriteLine("Int values before calling swap:");Console.WriteLine("a = {0}, b = {1}", a, b);Console.WriteLine("Char values before calling swap:");Console.WriteLine("c = {0}, d = {1}", c, d);
?// 調用 swapSwap<int>(ref a, ref b);Swap<char>(ref c, ref d);
?// 在交換之后顯示值Console.WriteLine("Int values after calling swap:");Console.WriteLine("a = {0}, b = {1}", a, b);Console.WriteLine("Char values after calling swap:");Console.WriteLine("c = {0}, d = {1}", c, d);Console.ReadKey();}}
}
結果:
Int values before calling swap:
a = 10, b = 20
Char values before calling swap:
c = I, d = V
Int values after calling swap:
a = 20, b = 10
Char values after calling swap:
c = V, d = I
將a,b,c,d進行交換,a,b,c,d的類型并不相同。
泛型的委托
可以通過類型參數來定義泛型委托:
using System;
using System.Collections.Generic;
?
delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}
?public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}
?static void Main(string[] args){// 創建委托實例NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);// 使用委托對象調用方法nc1(25);Console.WriteLine("Value of Num: {0}", getNum());nc2(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}
結果:
Value of Num: 35
Value of Num: 175
匿名方法
委托是用于引用與其具有相同標簽的方法。換句話說,可以使用委托對象調用可由委托引用的方法。匿名方法 提供了一種傳遞代碼塊作為委托參數的技術。匿名方法是沒有名稱只有主體的方法。在匿名方法中您不需要指定返回類型,它是從方法主體內的 return 語句推斷的。
編寫匿名方法的語法
匿名方法是通過使用 delegate 關鍵字創建委托實例來聲明的。例如:
delegate void NumberChanger(int n);
NumberChanger nc = delegate(int x)
{Console.WriteLine("Anonymous Method: {0}", x);
};
代碼塊 Console.WriteLine("Anonymous Method: {0}", x); 是匿名方法的主體。
委托可以通過匿名方法調用,也可以通過命名方法調用,即,通過向委托對象傳遞方法參數。
其中,匿名方法的主體之后要加一個“;”
例如:
using System;
?
delegate void NumberChanger(int n);
namespace DelegateAppl
{class TestDelegate{static int num = 10;public static void AddNum(int p){num += p;Console.WriteLine("Named Method: {0}", num);}
?public static void MultNum(int q){num *= q;Console.WriteLine("Named Method: {0}", num);}
?static void Main(string[] args){// 使用匿名方法創建委托實例NumberChanger nc = delegate (int x){Console.WriteLine("Anonymous Method: {0}", x);};
?nc(10);
?nc = new NumberChanger(AddNum);
?nc(5);
?nc = new NumberChanger(MultNum);
?nc(2);Console.ReadKey();}}
}
結果:
Anonymous Method: 10
Named Method: 15
Named Method: 30
由于匿名方法沒有方法簽名,只有方法體,所以無法使用命名方法類似的 方法名(); 去調用,所以只能將由委托變量去調用它,換言之,匿名方法將自己唯一擁有的方法主體交給委托,讓委托代理執行。