C#委託

來源:互聯網
上載者:User

標籤:class   blog   code   http   tar   ext   

委託的定義與方法的定義類似,只是傳回值類型的前面多了個delegate。如:public delegate void PrientDelegate(string content),其實委託就是一個能夠將方法封裝成一個參考型別,能和普通對象一樣傳遞。

一、委託聲明的注意點

  1、和類一樣,委託的聲明可以再類的外部聲明,委託也可以再類的內部聲明。

  2、委託的聲明雖然形式上與方法很像,但是它沒有方法主體,而是直接以分號結尾。

  3、修飾符可以是new、public、protected、internal、private。

  4、delegate關鍵字後是本委託類型可以匹配的方法簽名,尤其需要注意的是,它還包括方法的傳回型別,但並非與方法名完全符合。

二、給委託Binder 方法時,要符合的兩點要求

  1、方法的簽名要與委託一樣,方法參數的個數和類型。

  2、方法的傳回值類型要與委託一樣。

調用委託就相當於調用委託所綁定的方法, 一個委託可以綁定多個方法。委託是參考型別,因此它就可以到處傳遞,沒有它,C#能夠將方法到處傳遞嗎?

先附上一個基礎的例子

    public delegate void Print(string str);  //聲明一個委託    class Program    {        public static void PrintString(string str)        {            Console.WriteLine(str);        }        static void Main(string[] args)        {            //建立一個委派物件,並將方法PrintString賦給該委託            Print p = new Print(Program.PrintString);            //Print p = Program.PrintString;    //直接用=號也可以讓委託Binder 方法。            p("調用委託實際等於調用了委託所綁定的方法!");  //輸出調用委託實際等於調用了委託所綁定的方法!            Console.ReadKey();        }    }
三、委託的組合(多個委託之間的關係)

  "+"可以將兩個委託組合起來,"-"可以從一個委託組合中刪除其中一個成員。

    public delegate void Print();    class Program    {        public static void PrintString1()        {            Console.WriteLine("我是第一個方法");        }        public static void PrintString2()        {            Console.WriteLine("我是第二個方法");        }        static void Main(string[] args)        {            //建立一個委派物件並Binder 方法            Print p1 = Program.PrintString1;    //委託p1Binder 方法1            Print p2 = Program.PrintString2;    //委託p2Binder 方法2            Print p3 = p1 + p2; //將p1委託和p2委託組合成一個新委託p3            p3();       //調用委託p3,相當於調用p3所有已經綁定的方法,此時是方法1與方法2,此時輸出 我是第一個方法 "換行" 我是第二個方法            Print p4 = p3 - p1;    //建立一個新的委派物件,其綁定的方法為p3委託組合去除委託p1,實際就只剩下p2            p4();       //調用委託p4,相當於調用所有p4綁定的方法 輸出 我是第二個方法            Console.ReadKey();        }    }
四、委託的添加方法與刪除方法(一個委託的事情)

  通過"+="符可以像已有委託新添加綁定一個方法,通過"-="可以從委託中刪除一個已綁定的方法。

    public delegate void Print();    class Program    {        public static void PrintString1()        {            Console.WriteLine("我是第一個方法");        }        public static void PrintString2()        {            Console.WriteLine("我是第二個方法");        }        static void Main(string[] args)        {            Print p = PrintString1; //聲明一個委託並綁定第一個方法            p();    //調用委託p  輸出 我是第一個方法            p += PrintString2;  //再為p多綁定一個方法            p();    //調用委託p 輸出我是第一個方法 "換行" 我是第二個方法,看到現在p委託一經是綁定兩個方法的了            p -= PrintString1;  //從委託p中刪除一個已經綁定的方法p1;            p();    //調用委託p 輸出 我是第二個方法  看到p現在已經是只綁定第二個方法了            Console.ReadKey();        }    }

 

五、委託在回調中的應用

  委託在回呼函數中的調用:

    public delegate void PrintCompleteCallback(string message);    class Program    {        public static void PrintMessage(string message)        {            Console.WriteLine(message);        }        static void Main(string[] args)        {            PrintCompleteCallback CompleteCallback = Program.PrintMessage;  //建立一個委託,並綁定PrintMessage方法            Printer printer = new Printer("我是一個印表機");            printer.Print(CompleteCallback);  //調用p對象的Print方法,要求傳入一個委託作為參數,相當於傳入一個方法作為參數            Console.ReadKey();        }    }    public class Printer    {        private string _name;        public Printer(string name)        {            _name = name;        }        public void Print(PrintCompleteCallback callback)  //委派物件作為方法參數        {            callback(_name + "列印完畢");   //調用委託,相當於調用callback委託所綁定的對象        }    }

 

六、匿名方法

  匿名方法最大的優勢在於其減少了系統開銷,方法僅在委託使用時才定義。直接給委託傳遞一段代碼比先建立一個方法再綁定到委託要簡單。

  匿名方法的使用規則:

  1、匿名方法中不能使用跳躍陳述式跳至此匿名方法的外部,反之亦然;匿名方法外部的跳躍陳述式也不能跳到此匿名方法的內部。

  2、在匿名方法內部不能訪問不安全的代碼。另外,也不能訪問在匿名方法外部定義的ref與out參數。但可以使用在匿名方法外部定義的其它變數。

    public delegate void Print();   //定義一個委託    public delegate int GetLength(string str);  //定義一個帶參數的委託    class Program    {        static void Main(string[] args)        {            Print p = delegate      //不帶參數的匿名方法            {                Console.WriteLine("你在他鄉還好嗎?");            };            p();            GetLength g = delegate(string str)  //帶參數的匿名方法,注意方法的參數要與委託一致            {                return str.Length;            };            int length = g("你還好嗎?");    //調用委託,注意要傳入參數            Console.WriteLine(length);            Console.ReadKey();        }    }

 

七、Lambda運算式

  Lambda運算式和匿名方法基本相同,只是文法不同而已,可以說,Lambda運算式就是匿名方法。必須要記住,Lambda就是一個匿名方法,是給委託賦值用的,也就是說,使用Lambda的情境只有給委託賦值。

  Lambda運算式的文法更加簡潔:

  (param)=>expr  //其中param是一個輸出參數列表,說白了就是給委託的參數,expr是一個運算式或一系列語句,說白了就是輸出或執行的操作

  Lambda運算式具有以下特性:

  1、在一個具有唯一的顯式型別參數的Lambda運算式中,圓括弧可以從參數列表中刪除。例如:param=>expr

  2、當輸入參數不唯一時,括弧不能省略。

  3、輸入參數列表中的各參數可以顯式指定類型,也可以省略掉參數類型,具體類型通過類型推斷機制判斷。

  4、expr可以只包含一個計算運算式,也可以包含一系列語句,只是如果是一系列語句必須包括在大括弧內。

 一個委託參數的Lambda運算式

    public delegate string Print(string content);   //定義一個委託,該委託接受一個字串輸入,用於Lambda的左邊    class Program    {        static void Main(string[] args)        {            Print p = (str) => str += "-輸出參數";  //(str) 輸入參數,也就是委託參數中的content,箭頭後的是輸出,最簡單的理解就是,箭頭左邊是輸入,箭頭左邊是輸出,當然語句就不同了。            string outstr = p("輸入參數");            Console.WriteLine(outstr);  //輸出 輸入參數-輸出參數            Console.ReadKey();        }    }

  兩個委託參數的Lambda運算式的調用:

    public delegate string Print(string content1,string content2);   //定義一個委託    class Program    {        static void Main(string[] args)        {            Print p = (str1,str2) => str1 += "左邊-右邊" + str2;  //多個輸入參數,括弧不能省略            string outstr = p("張飛","關羽");   //輸出    張飛左邊-右邊關羽            Console.WriteLine(outstr);  //輸出 輸入參數-輸出參數            Console.ReadKey();        }    }

  帶語句的Lambda運算式

    public delegate void Print(string content);   //定義一個委託    class Program    {        static void Main(string[] args)        {            Print p = str =>  //帶語句的Lambda運算式,由於只有一個參數,所以小括弧可以省略            {                Console.WriteLine(str);            };            p("你好");    //你好            Console.ReadKey();        }    }

   2014-04-08-----------------------------

  現在學完了Lambda運算式了,可以結合擴充方法來瞧瞧Linq是怎麼實現的了。下面寫個模仿Linq的Where實現的樣本:

namespace ConsoleApplication3{    public delegate bool Compare(int i);    class Program    {        static void Main(string[] args)        {            //.Net的Linq擴充的是IEnumerable<TSource>(底層,範圍廣),而我是Calculate類            Calculate c = new Calculate();            int[] intArr = new int[] { 10, 20 };            int[] intArrReturn = c.Where(m => m > 18, intArr);            foreach (int i in intArrReturn)            {                Console.WriteLine(i);            }            Console.ReadKey();        }    }    public static class Exten    {        //技術點1:擴充方法,擴充的是Calculate類(第一個參數)        //技術點2:以委託作為參數,在方法內部會調用(第二個參數)        public static int[] Where(this Calculate c, Compare compare, int[] intArr)        {            int[] returnArr = new int[2];  //樣本定長數組            int count = 0;            foreach (int i in intArr)            {                if (compare(i))                   {                     returnArr[count] = i;                    count++;                }            }            return returnArr;        }    }    //計算類    public class Calculate    {            }}

  有那麼點像了,Linq是泛型+Lambda,所以它的名字比較帥,如果我們把委託(參數名)改一下:Compare改為Func<T>

  public delegate bool Func<T>(int i);

  看下代碼提示,是不是更像Linq了?

  

  很遺憾,到目前為止,我們擴充的Calculate類不支援鏈式操作,因為返回的結果是一個int[]數組。而鏈式操作的原理是,返回的結果又能繼續,如IEnumerable<int>的Where(m => m.xx)之後返回的又是IEnumerable<int>,所以能夠無限下去。

  2014-04-08-----------------------------------

八、委託中的協變與逆變

  協變:委託方法的傳回值類型直接或間接地繼承自委託簽名的傳回值類型,稱為協變。

  逆變:委託簽名中的參數類型繼承自委託方法的參數類型,稱為逆變。

  注意:協變強調的是傳回型別,逆變強調的則是方法參數。

   協變Demo:

  delegate People getSon(string name);    class Program    {        static void Main(string[] args)        {            getSon g = sons;            People p = g("張飛");     //注意雖然委託方法返回的是Son,但是委託本身返回的是父類People            Console.WriteLine(p.Name);  //輸出    張飛            Console.ReadKey();        }        public static Son sons(string name)     //委託方法的傳回值類型繼承自委託簽名的傳回值類型  逆變        {            Son m = new Son();            m.Name = name;            return m;        }    }    public class People    {        public People() { }        public int Id { get; set; }        public string Name { get; set; }    }    public class Son : People    {        public int Age { get; set; }    }

  逆變Demo:

  delegate string getSonName(Son s);    class Program    {        static void Main(string[] args)        {            getSonName g = sons;            Son s = new Son();            s.Name = "關羽";            string str = g(s);            Console.WriteLine(str);     //輸出    關羽            Console.ReadKey();        }        public static string sons(People p)     //委託簽名的參數類型繼承自委託方法的參數類型 此People為委託簽名參數Son的父類        {            return p.Name;        }    }    public class People    {        public People() { }        public int Id { get; set; }        public string Name { get; set; }    }    public class Son : People    {        public int Age { get; set; }    }
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.