日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > C# >内容正文

C#

C#进阶之路(一):委托

發(fā)布時(shí)間:2025/3/21 C# 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#进阶之路(一):委托 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、什么是委托

  簡(jiǎn)單說(shuō)它就是一個(gè)能把方法當(dāng)參數(shù)傳遞的對(duì)象,而且還知道怎么調(diào)用這個(gè)方法,同時(shí)也是粒度更小的“接口”(約束了指向方法的簽名)

  委托是一個(gè)類,它定義了方法的類型,使得可以將方法當(dāng)作另一個(gè)方法的參數(shù)來(lái)進(jìn)行傳遞,是種將方法動(dòng)態(tài)地賦給參數(shù)的做法。

  用過(guò)C/C++的,對(duì)委托不會(huì)陌生,委托可以看成函數(shù)指針的升級(jí)版本!

  函數(shù)指針簡(jiǎn)介:

  下面是一段C程序,Calc就是定義的函數(shù)指針。

typedef int (* Calc)(int a, int b);int Add(int a, int b) { int result = a + b; return result; } main() { int x = 100;int y = 200;int z = 0;Calc funcPoint1 = &Add;z = funcPoint1(x, y);printf("%d \n", z); }

這段程序很好的體現(xiàn)了一切皆地址的思想,變量和函數(shù)都是地址。

直接調(diào)用和間接調(diào)用的效果是一致的,都是訪問(wèn)那個(gè)內(nèi)存地址,委托相當(dāng)于函數(shù)指針的升級(jí)版。

  委托的簡(jiǎn)單案例

  一個(gè)委托類型定義了該類型的實(shí)例能調(diào)用的一類方法,這些方法含有同樣的返回類型和同樣參數(shù)(類型和個(gè)數(shù)相同)。

委托是一個(gè)類,所以要在類聲明的位置進(jìn)行聲明,而不是寫在類里面,那樣就寫成嵌套類了。如下定義了一個(gè)委托類型 - Calculator:

delegate int Calculator (int x);

此委托適用于任何有著int返回類型和一個(gè)int類型參數(shù)的方法,如:

static int Double (int x) { return x * 2; }

創(chuàng)建一個(gè)委托實(shí)例,將該此方法賦值給該委托實(shí)例:

Calculator c = new Calculator(Double);

也可以簡(jiǎn)寫成:

Calculator c = Double;

這個(gè)方法可以通過(guò)委托調(diào)用:

int result = c(2);

下面是完整代碼:

delegate int Calculator(int x);class Program {static int Double(int x) { return x * 2; }static void Main(string[] args) {Calculator c = Double;//c 就是委托實(shí)例,int result = c(2);Console.Write(result);Console.ReadKey();} }

二、委托的一般使用

2.1用委托實(shí)現(xiàn)插件式編程

  我們可以利用“委托是一個(gè)能把方法作為參數(shù)傳遞的對(duì)象”這一特點(diǎn),來(lái)實(shí)現(xiàn)一種插件式編程。

  例如,我們有一個(gè)Utility類,這個(gè)類實(shí)現(xiàn)一個(gè)通用方法(Calculate),用來(lái)執(zhí)行任何有一個(gè)整型參數(shù)和整型返回值的方法。這樣說(shuō)有點(diǎn)抽象,下面來(lái)看一個(gè)例子:

delegate int Calculator(int x);//這里定義了一個(gè)委托 class Program {static int Double(int x) { return x * 2; }static void Main(string[] args) {int[] values = { 1,2,3,4};Utility.Calculate(values, Double);foreach (int i in values)Console.Write(i + " "); // 2 4 6 8 Console.ReadKey();} }class Utility { public static void Calculate(int[] values, Calculator c) { // Calculator c 是簡(jiǎn)單委托的變種寫法,就是把實(shí)例化放在了形參定義的語(yǔ)句里 //但是這個(gè)實(shí)例化具體對(duì)應(yīng)的是什么方法,只有真的傳入?yún)?shù)的時(shí)候才知道!for (int i = 0; i < values.Length; i++)values[i] = c(values[i]);} }

  這個(gè)例子中的Utility是固定不變的,程序?qū)崿F(xiàn)了整數(shù)的Double功能。我們可以把這個(gè)Double方法看作是一個(gè)插件,如果將來(lái)還要實(shí)現(xiàn)諸如求平方、求立方的計(jì)算,我們只需向程序中不斷添加插件就可以了。

  如果Double方法是臨時(shí)的,只調(diào)用一次,若在整個(gè)程序中不會(huì)有第二次調(diào)用,那么我們可以在Main方法中更簡(jiǎn)潔更靈活的使用這種插件式編程,無(wú)需先定義方法,使用λ表達(dá)式即可,如:

...

Utility.Calculate(values, x => x * 2);

...

2.2多播委托

一個(gè)委托實(shí)例不僅可以指向一個(gè)方法,還可以指向多個(gè)方法。例如:

MyDelegate d = MyMethod1; // “+=” 用來(lái)添加,同理“-=”用來(lái)移除。 d += MyMethod2; // d -= MyMethod2

調(diào)用時(shí),按照方法被添加的順序依次執(zhí)行。注意,對(duì)于委托,+= 和 -= 對(duì)null是不會(huì)報(bào)錯(cuò)的,如:

MyDelegate d; d += MyMethod1;// 相當(dāng)于MyDelegate d = MyMethod1;

  為了更好的理解多播在實(shí)際開(kāi)發(fā)中的應(yīng)用,我用模擬瞬聘網(wǎng)的職位匹配小工具來(lái)做示例。在職位匹配過(guò)程中會(huì)有一段處理時(shí)間,所以在執(zhí)行匹配的時(shí)候要能看到執(zhí)行的進(jìn)度,而且還要把執(zhí)行的進(jìn)度和執(zhí)行情況寫到日志文件中。在處理完一個(gè)步驟時(shí),將分別執(zhí)行兩個(gè)方法來(lái)顯示和記錄執(zhí)行進(jìn)度。

  我們先定義一個(gè)委托(ProgressReporter),然后定義一個(gè)匹配方法(Match)來(lái)執(zhí)行該委托中的所有方法。如下:

public delegate void ProgressReporter(int percentComplete); public class Utility {public static void Match(ProgressReporter p) {if (p != null) {for (int i = 0; i <= 10; i++) {p(i * 10);System.Threading.Thread.Sleep(100); //線程暫停0.1s之后再繼續(xù)運(yùn)行程序! }}} }

然后我們需要兩個(gè)監(jiān)視進(jìn)度的方法,一個(gè)把進(jìn)度寫到Console,另一個(gè)把進(jìn)度寫到文件。如下:

class Program {static void Main(string[] args) {ProgressReporter p = WriteProgressToConsole;p += WriteProgressToFile;Utility.Match(p);Console.WriteLine("Done.");Console.ReadKey();}static void WriteProgressToConsole(int percentComplete) {Console.WriteLine(percentComplete+"%");}static void WriteProgressToFile(int percentComplete) {System.IO.File.AppendAllText("progress.txt", percentComplete + "%");}}

運(yùn)行結(jié)果:

?

看到這里,是不是發(fā)現(xiàn)你已然更加愛(ài)上C#了。

2.3靜態(tài)方法和實(shí)例方法對(duì)于委托的區(qū)別

  當(dāng)一個(gè)類的實(shí)例的方法被賦給一個(gè)委托對(duì)象時(shí),在上下文中不僅要維護(hù)這個(gè)方法,還要維護(hù)這個(gè)方法所在的實(shí)例。System.Delegate 類的Target屬性指向的就是這個(gè)實(shí)例。(也就是要在內(nèi)存中維護(hù)這個(gè)實(shí)例,也就是可能的內(nèi)存泄漏)

  但對(duì)于靜態(tài)方法,System.Delegate 類的Target屬性是Null,所以將靜態(tài)方法賦值給委托時(shí)性能更優(yōu)。

2.4泛型委托

如果你知道泛型,那么就很容易理解泛型委托,說(shuō)白了就是含有泛型參數(shù)的委托,例如:

public delegate T Calculator<T> (T arg);

我們可以把前面的例子改成泛型的例子,如下:

public delegate T Calculator<T>(T arg); class Program {static int Double(int x) { return x * 2; }static void Main(string[] args) {int[] values = { 1, 2, 3, 4 };Utility.Calculate(values, Double);foreach (int i in values)Console.Write(i + " "); // 2 4 6 8 Console.ReadKey();} } class Utility {public static void Calculate<T>(T[] values, Calculator<T> c) {for (int i = 0; i < values.Length; i++)values[i] = c(values[i]);} }

2.5Func 和 Action 委托

有了泛型委托,就有了能適用于任何返回類型和任意參數(shù)(類型和合理的個(gè)數(shù))的通用委托,Func 和 Action。如下所示(下面的in表示參數(shù),out表示返回結(jié)果):

delegate TResult Func <out TResult> ();

delegate TResult Func <in T, out TResult> (T arg);

delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2);

... 一直到 T16

delegate void Action ();

delegate void Action <in T> (T arg);

delegate void Action <in T1, in T2> (T1 arg1, T2 arg2);

... 一直到 T16

有了這樣的通用委托,我們上面的Calculator泛型委托就可以刪掉了,示例就可以更簡(jiǎn)潔了:

public static void Calculate<T>(T[] values, Func<T,T> c) {for (int i = 0; i < values.Length; i++)values[i] = c(values[i]); } //Func 是對(duì)delegate的一種簡(jiǎn)寫,更簡(jiǎn)潔

Func 和 Action 委托,除了ref參數(shù)和out參數(shù),基本上能適用于任何泛型委托的場(chǎng)景,非常好用。ACTION 和FUNC 最常用的兩種委托,類庫(kù)為我們準(zhǔn)備好的!

  action就是一種委托的簡(jiǎn)便寫法,默認(rèn)的是無(wú)返回值類型的方法,注意不要加括號(hào),只是綁定地址,而不是執(zhí)行!Func這種用來(lái)調(diào)用有返回值的委托!

直接調(diào)用方法,使用calculator.report();

間接調(diào)用,使用action.Invoke();

action();這種寫法是為了模仿函數(shù)指針的寫法。實(shí)際上還是默認(rèn)調(diào)用invoke()

上面的案例寫的有些亂,而且是一種把func寫入函數(shù)參數(shù)的類型,不容易理解,下面再用另一個(gè)案例

static void Main(string[] args) {Func<string> RetBook = new Func<string>(FuncBook);Console.WriteLine(RetBook()); } public static string FuncBook() {return "送書(shū)來(lái)了"; }

無(wú)返回值

static void Main(string[] args) {Action<string> BookAction = new Action<string>(Book);BookAction("百年孤獨(dú)"); } public static void Book(string BookName) {Console.WriteLine("我是買書(shū)的是:{0}",BookName); }

2.6委托的異步調(diào)用

1、顯式異步調(diào)用

顯式異步調(diào)用 thread ?或者 task

?

2、隱式異步調(diào)用

使用委托進(jìn)行隱式異步調(diào)用,begininvoke就是隱式異步調(diào)用,它會(huì)開(kāi)發(fā)分支線程,他有兩個(gè)參數(shù)。

aciont1.BeginInvoke(null, null);

EndInvoke

隱式調(diào)用也有兩種,一種是不使用回調(diào)函數(shù)的,另一種是使用的。

不使用回調(diào)函數(shù)

namespace delegate3 {class Program{//public delegate int AddHandler(int a, int b);public class Cal{public static int Add(int a, int b){Console.WriteLine("開(kāi)始計(jì)算:" + a + "+" + b);Thread.Sleep(3000); //模擬該方法運(yùn)行三秒Console.WriteLine("計(jì)算完成!");return a + b;}}static void Main(string[] args){Console.WriteLine("===== 異步調(diào)用 AsyncInvokeTest =====");//AddHandler handler = new AddHandler(Cal.Add);//IAsyncResult: 異步操作接口(interface)//BeginInvoke: 委托(delegate)的一個(gè)異步方法的開(kāi)始//IAsyncResult result = handler.BeginInvoke(1, 2, null, null);Console.WriteLine("繼續(xù)做別的事情。。。");Func<int,int,int> RetBook = new Func<int,int,int>(Cal.Add);//RetBook.BeginInvoke(1, 2);IAsyncResult result = RetBook.BeginInvoke(1, 2, null, null);//異步操作返回 Console.WriteLine(RetBook.EndInvoke(result));Console.ReadKey();}} } View Code

使用回調(diào)函數(shù)

namespace CallBack { public delegate int AddHandler(int a, int b);public class Cal{public static int Add(int a, int b){Console.WriteLine("開(kāi)始計(jì)算:" + a + "+" + b);Thread.Sleep(3000); //模擬該方法運(yùn)行三秒Console.WriteLine("計(jì)算完成!");return a + b;}}class Program{static void Main(){Console.WriteLine("===== 異步回調(diào) AsyncInvokeTest =====");AddHandler handler = new AddHandler(Cal.Add);//異步操作接口(注意BeginInvoke方法的不同!)IAsyncResult result = handler.BeginInvoke(1, 2, new AsyncCallback(回調(diào)函數(shù)), "AsycState:OK");Console.WriteLine("繼續(xù)做別的事情。。。");Console.ReadKey();}static void 回調(diào)函數(shù)(IAsyncResult result){//result 是“加法類.Add()方法”的返回值//AsyncResult 是IAsyncResult接口的一個(gè)實(shí)現(xiàn)類,空間:System.Runtime.Remoting.Messaging//AsyncDelegate 屬性可以強(qiáng)制轉(zhuǎn)換為用戶定義的委托的實(shí)際類。AddHandler handler = (AddHandler)((AsyncResult)result).AsyncDelegate;Console.WriteLine(handler.EndInvoke(result));Console.WriteLine(result.AsyncState);}} } View Code

三、委托的缺點(diǎn)

?

引用了某個(gè)方法,那么這個(gè)方法在內(nèi)存中就不能釋放了,一旦釋放,委托就不能調(diào)用這個(gè)方法,所以委托有可能造成內(nèi)存泄漏。(靜態(tài)方法不存在這個(gè)問(wèn)題)

?

轉(zhuǎn)載于:https://www.cnblogs.com/qixinbo/p/8297314.html

總結(jié)

以上是生活随笔為你收集整理的C#进阶之路(一):委托的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。