首頁  >  文章  >  後端開發  >  C#中的委託實例代碼詳解(圖文)

C#中的委託實例代碼詳解(圖文)

黄舟
黄舟原創
2017-03-29 11:40:431674瀏覽

本文主要介紹了在C#中委託的相關知識。具有一定的參考價值,下面跟著小編一起來看下吧

委託這個東西不是很好理解,可是工作中又經常用到,你隨處可以看到它的身影,真讓人有一種又愛又恨的感覺,我相信許多人被它所困擾過。

一提到委託,如果你學過C語言,你一定會馬上聯想到函數指標

什麼是委託?委託是C#中類型安全性的,可以訂閱一個或多個具有相同簽章方法的函數指標。委託可以把函數做為參數傳遞,其實際意義就是讓別人代理你的事情。委託可以看做是函數的指針,整數可以用整數變數指向它,物件可以用物件變數指向它,

函數也可以用委託變數指向它。我們可以選擇將委託類型看做只定義了一個方法的介面,而委託的實例可以看做是實作了那個介面的一個物件。

使用委託,必須滿足4個條件:

  • 聲明委託類型;

  • ##必須有一個方法包含了要執行的程式碼;

  • 必須建立一個委託實例;

  • 必須呼叫(invoke)委託實例。

所委託的申明

宣告委託的方式:delegate 傳回值型別委託類型名稱(參數)

所委託的申明和介面方法的申明基本上一致,只是在回傳型別關鍵字的前面多了一個delegate關鍵字。還有就是委託一般聲明為public類型,因為它隨時要供別人調用的。

委託的本質也是一個型別。我們聲明一個類別可以進行實例化,同樣委託也可以進行實例化。

有以下四種委託:

//1.无参数无返回值
    public delegate void NoParaNoReturnEventHandler();
    //2.有参数无返回值
    public delegate void WithParaNoReturnEventHandler(string name);
    //3.无参数有返回值
    public delegate string NoParaWithReturnEventHandler();
    //4.有参数有返回值
    public delegate string WithParaWithReturnEventHandler(string name);

如果程式碼想要執行操作,但不知道操作細節,一般可以使用委託。例如, Thread類別之所以知道要在一個新執行緒裡執行什麼,唯一的原因就是在啟動新執行緒時,向它提供了一個ThreadStart或ParameterizedThreadStart委託實例。

Thread th = new Thread(Test);
th.Start();
public Thread(ThreadStart start);
public delegate void ThreadStart();

ThreadStart是一個無參無回傳值的委託。

    static void Test()
    {
      Console.WriteLine("线程方法");
    }

這個Test方法的函式簽章必須和委託ThreadStart的函式簽章一致。

委託的呼叫

必須先實例化委託,然後再呼叫。

函數的簽章和委託的簽章必須一致。

NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo;,編譯器幫我們進行了new,但是不能寫成NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo();

##因為這樣就變成函數呼叫了。

#region 无返回值委托调用
    public static void Show()
    {
      //实例化委托
      NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = new NoParaNoReturnEventHandler(ConsoleInfo);
      //NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo; //简写
      //委托调用 通过Invoke()调用,或者可以直接省略
      _NoParaNoReturnEventHandler.Invoke();
      //_NoParaNoReturnEventHandler();
    }
    private static void ConsoleInfo()
    {
      Console.WriteLine("无参数无返回值的函数调用");
    } 
    #endregion

沒有委託就沒有異步,非同步正是因為委託的存在。

_NoParaNoReturnEventHandler.BeginInvoke(null,null); //非同步呼叫

為什麼要使用委託##我們完全可以直接呼叫方法,為什麼還需要透過一個委託來呼叫呢?委託有什麼意義?

解耦,對修改關閉,對擴展開放。邏輯分離。

你可以把委託理解為函數的父類,或是一個方法的佔位符。

我們來看下程式碼,假設有2個方法,一個說英語,一個說漢語,而這2個方法的函數簽章是一樣的。

public static void SayChinese(string name)
    {
      Console.WriteLine("你好," + name);
    }
    public static void SayEnglish(string name)
    {
      Console.WriteLine("hello," + name);
    }

那麼我們在外部呼叫的時候,

  MyDelegate.SayChinese("张三");
  MyDelegate.SayEnglish("zhangsan");

如果要呼叫這兩個不同的方法,是不是要寫不同的呼叫程式碼

我們能不能只一個方法呼叫呢?修改程式碼如下:

public static void Say(string name,WithParaNoReturnEventHandler handler)
    {
      handler(name);
    }
   public static void SayChinese(string name)
    {
      Console.WriteLine("你好," + name);
    }
    public static void SayEnglish(string name)
    {
      Console.WriteLine("hello," + name);
    }

這樣,只透過一個方法Say來進行呼叫。

如何呼叫呢?以下三種呼叫方式:

      WithParaNoReturnEventHandler _WithParaNoReturnEventHandler = new WithParaNoReturnEventHandler(MyDelegate.SayChinese);
      MyDelegate.Say("张三",_WithParaNoReturnEventHandler);
      MyDelegate.Say("张三", delegate(string name) { Console.WriteLine("你好," + name); }); //匿名方法
      MyDelegate.Say("张三", (name) => { Console.WriteLine("你好," + name); }); //lambda表达式

以上程式碼使用了幾種呼叫方式,這些呼叫方式都是隨著C#的升級而不斷優化的。第一種是C#1.0中就存在的傳統呼叫方式,第二種是C#2.0中的匿名方法呼叫方式,所謂匿名方法,就是沒有名字的方法,當方法只呼叫一次時使用匿名方法最合適不過了。 C#3中的lambda表達式。其實泛型委託同樣是被支援的,而.NET 3.5則更進一步,引入了一組名為Func的泛型委託類型,它能取得多個指定類型的參數,並傳回另一個指定類型的值。

lambda表達式

lambda表達式的本質就是一個方法,一個匿名方法。

如果方法體只有一行,無回傳值,還可以去掉大括號和分號。

MyDelegate.Say("张三", (name) => Console.WriteLine("你好," + name));

如果方法体只有一行,有返回值,可以去掉大括号和return。

WithParaWithReturnEventHandler _WithParaWithReturnEventHandler = (name)=>name+",你好";

从.NET3.5开始,基本上不需要我们自己来申明委托了,因为系统有许多内置的委托。

Action和Func委托,分别有16个和17个重载。int表示输入参数,out代表返回值,out参数放置在最后。

Action表示无返回值的委托,Func表示有返回值的委托。因为方法从大的角度来分类,也分为有返回值的方法和无返回值的方法。

也就是说具体调用什么样的方法,完全由调用方决定了,就有了更大的灵活性和扩展性。为什么这么说,如果我有些时候要先说英语再说汉语,有些事时候要先说汉语再说英语,如果没有委托,我们会怎么样实现?请看如下代码:

public static void SayEnglishAndChinese(string name)
    {
      SayEnglish(name);
      SayChinese(name);
    }
    public static void SayChineseAndEnglish(string name)
    {
      SayChinese(name);
      SayEnglish(name);
    }

如果又突然要添加一种俄语呢?被调用方的代码又要修改,如此循环下去,是不是要抓狂了?随着不断添加新语种,代码会变得越来越复杂,越来越难以维护。这样的代码耦合性非常高,是不合理的,也就是出现了所谓的代码的坏味道,你可以通过设计模式(如观察者模式等),在不使用委托的情况下来重构代码,但是实现起来是非常麻烦的,要写很多更多的代码...

委托可以传递方法,而这些方法可以代表一系列的操作,这些操作都由调用方来决定,就很好扩展了,而且十分灵活。我们不会对已有的方法进行修改,而是只以添加方法的形式去进行扩展。

可能有人又会说,我直接在调用方那里来一个一个调用我要执行哪些方法一样可以实现这样的效果啊?

可你有没有想过,你要调用的是一系列方法,你根本无法复用这一系列的方法。使用委托就不一样了,它好比一个方法集合的容器,你可以往里面增减方法,可以复用的。而且使用委托,你可以延时方法列表的调用,还可以随时对方法列表进行增减。委托对方法进行了再一次的封装。

总结:也就是当你只能确定方法的函数签名,无法确定方法的具体执行时,为了能够更好的扩展,以类似于注入方法的形式来实现新增的功能,就能体现出委托的价值。

委托和直接调用函数的区别:用委托就可以指向任意的函数,哪怕是之前没定义的都可以,而不用受限于哪几种。

多播委托

组合的委托必须是同一个类型,其相当于创建了一个按照组合的顺序依次调用的新委托对象。委托的组合一般是给事件用的,用普通委托的时候很少用。

通过+来实现将方法添加到委托实例中,-来从委托实例中进行方法的移除。

+和-纯粹是为了简化代码而生的,实际上其调用的分别是Delegate.Combine方法和Delegate.Remove。

如果委托中存在多个带返回值的方法,那么调用委托的返回值是最后一个方法的返回值。

public static void MultipleShow()
    {
      //多播委托
      NoParaWithReturnEventHandler _NoParaWithReturnEventHandler = new NoParaWithReturnEventHandler(GetDateTime);
      _NoParaWithReturnEventHandler += GetDateTime;
      Console.WriteLine(_NoParaWithReturnEventHandler());
    }
    public static string GetDateTime()
    {
      return string.Format("今天是{0}号。", DateTime.Now.Day.ToString());
    }

委托总结:

  • 委托封装了包含特殊返回类型和一组参数的行为,类似包含单一方法的接口;

  • 委托类型声明中所描述的类型签名决定了哪个方法可用于创建委托实例,同时决定了调用的签名;

  • 为了创建委托实例,需要一个方法以及(对于实例方法来说)调用方法的目标;

  • 委托实例是不易变的,就像String一样;

  • 每个委托实例都包含一个调用列表——一个操作列表;

  • 事件不是委託實例-只是成對的add/remove方法(類似屬性的取值方法/賦值方法)。

常見使用場景:窗體傳值、執行緒啟動時綁定方法、lambda表達式、非同步等等。

生活中的例子:現在不是大家都在搶火車票嗎,使用雲搶票就相當於使用委託,你可以直接自己買票,也可以託管於雲搶票,自己搶票的話,在快要開槍的時候,你必須時刻刷新,下單輸驗證碼等等,使用雲搶票的話,你只要放票前,提前輸入搶票信息,就再也不需要你管了,自動出票,你根本不需要知道雲搶票那邊是怎麼幫你實現搶票的。相同時間和車次可以做成一個委託實例,有很多人透過這個委託實例來進行搶票操作。

以上是C#中的委託實例代碼詳解(圖文)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn