c# delegate知識


一、引用方法

委托是尋址方法的.NET版本。委托是類型安全的類,它定義了返回類型和參數的類型。委托是對方法的引用,也可以對多個方法進行引用,委托可以理解為指向方法地址的指針。

如:delegate int ReturnIntHandler(int a,int b);//int是返回類型,a和b是引用類型,這是委托執行的方法必須滿足如下格式:int method(int param1,int param2);

 

二、委托

當要把方法傳遞給其它方法時,需要使用委托。委托是一種特殊類型的對象,其特殊之處在於,我們以前定義的所有對象都包含數據,而委托包含的只是一個或多個方法的地址。

 

1、聲明委托

委托使用關鍵字 delegate 進行定義。

定義委托基本上就是定義一個新類,所以可以在定義類的任何相同地方定義委托。可以在委托定義上應用常見的訪問修飾符:public、private、protected等。其訪問作用域也雷同於類。

 

2、使用委托

為了減少輸入量,只需要委托實例,就可以只傳遞地址的名稱。這稱為委托推斷。

delegate int CalculateMethodInvoker(int x, int y);
    class Program
    {
        static void Main(string[] args)
        {
            //創建委托對象
            CalculateMethodInvoker calculateMethodInvoker = CalculateHelper.Sum;
            //等同於CalculateMethodInvoker calculateMethodInvoker = new CalculateMethodInvoker(CalculateHelper.Sum);//委托的實例化,指向Sum方法,其實委托也是可以定義,實例化調用的,不只是lambda表達式的調用方式
            int x = 100, y = 200;
            Console.WriteLine("x,y相加:{0}", Calculate(calculateMethodInvoker, x, y));
            calculateMethodInvoker = CalculateHelper.Multiply;
            Console.WriteLine("x,y相乘:{0}", Calculate(calculateMethodInvoker, x, y));
            Console.ReadKey();
        }
        public static int Calculate(CalculateMethodInvoker calculateMethodInvoker, int x, int y)
        {
            //return calculateMethodInvoker(x, y);
            //return calculateMethodInvoker.Invoke(x, y);//是不是當前線程都可以
            IAsyncResult result = calculateMethodInvoker.BeginInvoke(x, y, null, calculateMethodInvoker);//異步,這里只是做展示,EndInvoke類似於async中的await,這里不能實現異步效果
            return calculateMethodInvoker.EndInvoke(result);
        }

    }
    public class CalculateHelper
    {
        public static int Sum(int x, int y)
        {
            return x + y;
        }
        public static int Multiply(int x, int y)
        {
            return x * y;
        }
    }

 

3、Action<T>和Func<T>委托

除了為每個參數和返回類型定義一個新的委托類型外,還可以使用Action<T>和Func<T>委托。

泛型Action<T>委托表示引用一個void返回類型的方法,沒有泛型參數的Action類可調用沒有參數的方法,如Action 等價於 delegate void mydelegate;  Action<int,int>等價於delegate void mydelegate(int param1,int param2);

泛型Func<T>委托表示引用一個有返回值的方法,泛型的最后一個參數時Func的返回值類型,如Func<int,int,bool>,等價於delegate bool mydelegate(int param1,int param2);

 

4、多播委托

委托也可以包含多個方法。這種委托成為多播委托。如果調用多播委托,就可以按順序連續調用多個方法。為此,委托的簽名就必須返回void;否則,就只能得到委托調用的最后一個方法的結果。多播委托識別運算符“-”、“+”、“-=”、“+=”以從委托中增加或刪除方法調用。

如:

class Program
    {
        static void Main(string[] args)
        {
            Action<int, int> calFunc = CalculateHelper.Sum;
            calFunc += CalculateHelper.Multiply;//多播加
            int x = 100, y = 200;
            Calculate(calFunc, x, y);
            calFunc =calFunc- CalculateHelper.Multiply;//多播減
            Calculate(calFunc, x, y);
            Console.ReadKey();
        }
        public static void Calculate(Action<int, int> calculateMethodInvoker, int x, int y)
        {
            Console.WriteLine("運行結果:");
            //calculateMethodInvoker(x, y);
            foreach (Action<int, int> item in calculateMethodInvoker.GetInvocationList())//遍歷,這里需要轉為為當前類型委托
            {
                try
                {
                    item(x, y);//執行委托指向方法
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

如果通過委托調用的其中一個方法拋出異常,整個迭代就會停止。解決的方法是,使用Delegate類中定義的GetInvocationList()方法獲取Delegate對象數組,再使用循環遍歷執行,在過程中捕獲異常,來繼續下一次迭代。

 

5、匿名方法

匿名方法是用作委托的參數的一段代碼。

如:

Action<int, int> calFunc = delegate (int i, int j)
{
    Console.WriteLine("x,y相加:{0}", i + j);
};

在匿名方法中不可使用跳轉語句(break、goto或continue),在匿名方法內部不能訪問不安全代碼,不能訪問在匿名方法外部使用的ref和out參數。

 

三、lambda表達式

C#3.0后,可以使用lambda把實現代碼賦予委托,只要有委托參數類型的地方,就可以使用lambda表達式。

如:

Action<int, int> calFunc = (i, j) =>
{
    Console.WriteLine("x,y相加:{0}", i + j);
};

 

1、參數

lambda表達式有幾種定義參數的方式。如果只有一個參數,只寫出參數名就足夠了。如果除一個參數以外,需要圓括號把參數名括起來。

例子:

Action<int> one = i =>
{
    //method body
};
Action<int, int> two = (i, j) =>
{
    //method body
};

 

2、多行代碼

如果lambda表示只有一條語句,在方法塊內就不需要花括號和return語句,因為編譯器會隱式添加return。

如:

Func<int> lambdaOne = () => 0;

如果實現代碼超過一行,就需要使用return語句顯式返回。

如:

{
    int i = 0;
    i++;
    ++i;
    return i;
};

 

3、閉包

通過lambda表達式可以訪問lambda表達式塊外部的變量。這稱為閉包。

如:

int param = 10;
Action<int> lambdaSecond = (i) =>
{
    Console.WriteLine(i + param);
};
lambdaSecond(3);
Console.ReadKey();

 

四、事件

事件基於委托,為委托提供了一種發布/訂閱機制。

如:

class Program
    {
        static void Main(string[] args)
        {
            AlarmClock alarmClock = new AlarmClock();
            Student zsStudent = new Student("張三");
            alarmClock.ItsGetUpClockEvent += zsStudent.ItsGetUpClock;
            alarmClock.ItsGetUpClock();
            Student lsStudent = new Student("李四");
            //WeakEventManager<AlarmClock, EventArgs>.AddHandler(alarmClock, "ItsGetUpClockEvent", lsStudent.ItsGetUpClock);
            ////弱事件,System.Windows,WPF中經常用到
            //alarmClock.ItsGetUpClock();
            Console.ReadKey();
        }

    }
    //事件發布類
    public class AlarmClock
    {
        public event EventHandler<EventArgs> ItsGetUpClockEvent;
        public void ItsGetUpClock()
        {
            Console.WriteLine("時間到,起床了!");
            ItsGetUpClockEvent?.Invoke(this, new EventArgs());//判斷是否訂閱事件
        }
    }
    //事件偵聽類
    public class Student
    {
        public string Name { get; set; }
        public Student(string name)
        {
            this.Name = name;
        }
        public void ItsGetUpClock(object sender, EventArgs e)
        {
            Console.WriteLine("{0}關掉鬧鍾,起床了。", Name);
        }
    }

事件最常用的地方是Winform和Wpf窗體中,而Invoke的經典使用場景如下(非當前線程更改窗體文本):

 private void ShowExecLog(string log)
        {
            if (this.richTextBox1.InvokeRequired)//判斷是否是當前線程
            {
                this.richTextBox1.Invoke(new ShowLogHandler(ShowLog), log);
            }
            else
            {
                this.richTextBox1.Text += log;
            }
        }

        public void ShowLog(string log)
        {
            this.richTextBox1.Text += log;
        }


        public delegate void ShowLogHandler(string log);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM