委托知識整理筆記
一、委托基礎
1.1 委托是什么
與類、枚舉、結構、接口一樣,委托也是一種類型。類是對對象的抽象,委托是對函數(shù)的抽象。一個委托代表了具有相同參數(shù)列表和返回值的(具有相同簽名(signature))所有函數(shù)。
1.2 為什么要使用委托?能給我?guī)砟男┖锰帲?/p>
有時在程序設計時,某個方法在執(zhí)行時可能需要根據不同情形做不同處理并且這些情形根據時間可能還會有變動,所以這部分代碼沒法寫死。所以C#就設計了委托來處理這些情形,將這些變化不定的細節(jié)交由委托來處理,委托來代理多個類似的處理方法。
這樣使用委托方法作為方法參數(shù)的做法,具體執(zhí)行細節(jié)由被代理方法完成的方式。可以避免在程序中大量使用If-Else(Switch)判斷語句,同時使得程序具有更好的可擴展性。
1.3 如何使用?使用方法步驟有哪些呢?
e.g:
1 //第一步.聲明一個委托 2 3 delegate int DelegateAdd(int a, int b); 4 5 public class Test 6 7 { 8 9 //第二步.提供要處理方法10 11 public static int Add(int a, int b)12 {13 14 int c = 0;15 16 c = a + b;17 18 return c;19 20 }21 22 }23 24 //入口類25 26 public class TestB27 28 {29 30 public static void Main()31 32 {33 34 //第三步.實例一個委托35 36 DelegateAdd da = new DelegateAdd (Test.Add);37 38 //4第四步調用委托39 40 int sum = da(2, 2);41 42 Console.WriteLine(sum.ToString());43 44 }45 46 }
上面寫法是一種形式(靜態(tài)方法或實例方法),下面兩種方式也可實現(xiàn):
匿名方法:
DelegateAdd da = delegate(int a, int b){
return a+b;
}
Lambda表達式方法:
DelegateAdd da = (a,b)=>a+b;
1.4 委托鏈
委托可以用 “+=” 和 “-=” 進行增加與刪除委托實例;當增加多個委托實例時就形成了委托鏈表,即委托鏈。調用時,按照方法被添加的順序依次執(zhí)行。
1.5 泛型委托
泛型委托支持在委托返回值和參數(shù)上應用參數(shù)類型。
delegate void GenericsDelegate<T>(T value); public class GenericsDelegateClass { public static void F(int i) { Console.WriteLine("調用 F(int i) 方法 i="+i); } public static void G(string s) { Console.WriteLine("調用 G(string s) 方法 s="+s); } public static void Test() { GenericsDelegate<string>p2=G; p2("test"); GenericsDelegate<int>p1= new GenericsDelegate<int>(F); p1(222); } }
幾個常用泛型委托
Action<T,…>是無返回值的泛型委托,這個委托類存在不同的變體,至少0個參數(shù),至多16個參數(shù);
Func<…T>是必須有返回值T的泛型委托,也存在不同的變體,至少0個參數(shù),至多16個參數(shù);
PRedicate <T>是返回值為bool的泛型委托,只有1個參數(shù);表示定義一組條件并確定指定對象是否符合這些條件的方法。此委托由 Array 和 List 類的幾種方法使用,用于在集合中搜索元素。
public class DelegateClass { public DelegateClass() { } public static void Print<T>(T v) { Console.WriteLine(v.ToString()); } public static int Add(int a, int b) { return a+b; } public static double AddTow(double a) { return a+ 2; } public static void Test() { Action<int>acInt= new Action<int>(Print<int>); acInt(3); //3 Action<string>acString=Print<string>; acString("hello string");//hello string Func<int, int, int>f1= new Func<int, int, int>(Add); Console.WriteLine(f1(2, 3));//5 Func<double, double>f2=AddTow; Console.WriteLine(f2(33.45));//35.45
List<int>list= new List<int>() { 1,2,3,4,5,6,7,8,9,10}; Predicate<int>p= new Predicate<int>((v) =>v< 5); var LessThanFive=list.Find(p); if (LessThanFive!= 0) { Console.WriteLine(LessThanFive);// 輸出第一匹配1 } Predicate<int>p1= new Predicate<int>((v) =>v>= 11); int NotLessThanTen=list.Find(p1); if (NotLessThanTen!= 0) { Console.WriteLine(NotLessThanTen); } else { Console.WriteLine("未找到符合條件的元素");//未找到符合條件的元素 } } }
二、委托的同步調用、異步調用與異步回調
同步調用
以上介紹的委托調用方法都是同步調用。其實也可以Invoke方法用來進行同步調用。
如:f1.Invoke(1,2);同步調用也可以叫阻塞調用,它將阻塞當前線程,然后執(zhí)行調用,調用完畢后再繼續(xù)向下進行。如果是要調用一項耗時操作,可能會讓程序停頓很長時間,造成糟糕的用戶體驗,這時候異步調用就很有必要了。
異步調用
異步調用不阻塞線程,而是把調用塞到線程池中,程序主線程或UI線程可以繼續(xù)執(zhí)行。委托的異步調用通過BeginInvoke和EndInvoke來實現(xiàn)。
public static void PrintList(List<int>l) { foreach (var one in l) { Console.WriteLine(one.ToString()); System.Threading.Thread.Sleep(300); } } public static void Test() { List<int>list= new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; Action<List<int>>acList=PrintList; acList.BeginInvoke(list,null,null); System.Threading.Thread.Sleep(600); Console.WriteLine("其他方法"); acList.EndInvoke(result); Console.WriteLine("其他操作");}
運行結果:
可以看到,主線程并沒有等待,而是直接向下運行了。但是問題依然存在,當主線程運行到EndInvoke時,如果這時調用沒有結束(這種情況很可能出現(xiàn)),這時為了等待調用結果,線程依舊會被阻塞。
異步回調
用回調函數(shù),當調用結束時會自動調用回調函數(shù),解決了為等待調用結果而讓線程依舊被阻塞的局面。
public static void PrintList(List<int>l) { foreach (var one in l) { Console.WriteLine(one.ToString()); System.Threading.Thread.Sleep(300); } } static void MyCallBack(IAsyncResultresult) { Action<List<int>>handler= (Action<List<int>>)result.AsyncState; handler.EndInvoke(result); Console.WriteLine("異步執(zhí)行完畢"); } public static void Test() { List<int>list= new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; Action<List<int>>acList=PrintList; IAsyncResultresult=acList.BeginInvoke(list, new AsyncCallback(MyCallBack), acList); System.Threading.Thread.Sleep(600); Console.WriteLine("其他方法");
//acList.EndInvoke(result); Console.WriteLine("其他操作");}
執(zhí)行結果:
三、應用場景
委托可應用于Observer模式(觀察者模式)、子窗口傳值、異步更新主界面(如:進度條)等。
以winform進度條為例:
namespace DelegateTest{ public delegate void DelegateMethod(ProgressBar pb, int position); public partial class MainForm : Form { public MainForm() { InitializeComponent(); } public void progressBarDel(ProgressBar pb, int position) { if (pb.InvokeRequired)//等待異步 { DelegateMethod fc=new DelegateMethod(progressBarDel); this.Invoke(fc,pb, position);//通過代理調用刷新方法 } else { this.progressBar1.Value=position; } } private void MainForm_Load(object sender, EventArgs e) { this.progressBar1.Maximum= 100; this.progressBar1.Value= 0; Thread t=new Thread(progressThread); t.Start(); } void progressThread() { for (int i= 1; i< 100; i++) { progressBarDel(this.progressBar1, i); System.Threading.Thread.Sleep(1000); } } }}
新聞熱點
疑難解答