1. 程式人生 > >C# 委托基礎

C# 委托基礎

http throw 當前 告訴 .com html cati 特殊 multi

不知不覺,又開始委托的學習了,感覺這個東西我能學一輩子,哈哈哈!這次看的是官方的參考書(C#高編9),每次看不同的資料,總能學到不同的知識!言歸正傳

1、為什麽要使用委托?

我們習慣於將數據作為參數傳遞給方法,但是很多時候我們需要將一個方法傳遞給另一個方法。所以委托就是幹這個的,將方法作為參數,傳遞給另一方法.

舉個例子:在C#中,可以告訴計算機並行執行某些新的執行序列,同時運行當前任務,這個序列就稱之為線程,如果要告訴計算機啟動一個新的線程,就必須告訴線程入口方法在哪裏,並告訴計算機開始啟動方法的細節,所以線程(Thread)的構造函數必須帶有一個參數,該參數定義了線程調用的方法。這裏就使用到了委托的概念,將方法傳遞給線程!

2、委托的特性

在C和C++中,只能提取函數的地址,該地址可以作為參數傳遞給任何需要它的函數,這種方法會導致類型安全問題。但是在.Net Framework中,當我們進行面向對象編程的時候,幾乎沒有方法是孤立存在的,當我們吊用一個方法前,必須確保這個方法與類實例關聯,所以在.Net Framework中不允許直接使用方法,或者傳遞方法,如果需要傳遞方法,那麽必須把該方法的細節和參數封裝在一個類對象中,通過傳遞對象實例的方式,來傳遞方法。但是用類對象來傳遞方法,有點大材小用了,所以.Net Framework中定義了一種新的類型對象-委托,這種對象專門用來傳遞方法,如果我們要傳遞一個方法,就必須把方法的細節封裝到委托中。通過委托實例來傳遞方法,所以委托和類其實在某種程度上說是一樣的,委托是一種特殊的類。這種類,專門負責傳遞方法。

3、聲明委托

2中,提到了委托其實是一種特殊的類,所以它的申明方式和類其實是差不多的,只不過類用class來修飾,且類有方法體,而委托使用delegate來修飾沒有方法體,委托必須聲明委托所代表的一類方法的返回值,並且聲明這類方法的參數,代碼如下:

public delegate void MyDeleagte(int a);

一個無返回值,帶一個int參數的委托申明好了。

如果要定一個委托,不帶參數,返回一個string類型的值,代碼如下:

public delegate string MyDelegate();

4、委托的結構

當我們定義完一個委托,實際上是定義了一個類,委托實現為派生自System.MulticastDelegate抽象類的類,System.MulticastDelegate又派生字System.Delegate,C#編譯器能識別這個類,會使用其委托語法.

5、委托中註冊的方法的使用

委托中方法的調用有兩種方式:

a、委托變量加括號的方式,代碼如下:

public delegate void GetString();
    class Program
    {
        static void Main(string[] args)
        {
            var a = new GetString(aa);
            a += aa;
            a += aa;
            a += aa;
            a += aa;
            a += aa;
            a();
            Console.ReadLine();
        }
        static void aa()
        {
            Console.WriteLine("1");
        }
    }

b、使用Invoke()方法

    public delegate void GetString();
    class Program
    {
        static void Main(string[] args)
        {
            var a = new GetString(aa);
            a += aa;
            a += aa;
            a += aa;
            a += aa;
            a += aa;
            a.Invoke();
            Console.ReadLine();
        }
        static void aa()
        {
            Console.WriteLine("1");
        }
    }

上面是最常規的委托使用方法,當然還有其他的如:BeginInvoke()等,這裏不做解釋,如想要了解,請參考C# 委托進階

6、多播委托

上面的實例中,我給一個委托綁定了多個方法,如果要多次調用這些方法,就需要顯示多次吊用。我們把一個委托,如果這個委托綁定了多個方法,稱為多播委托,如果調用多播委托,就可以按順序連續吊用多個方法。為此,委托的簽名必須返回void,否則就只能得到最後一個方法的返回值。

    class Program
    {
        static void Main(string[] args)
        {
            Action<double> options = new Action<double>(MutiplyByTwo);
            options += Square;
            options.Invoke(3);
            Console.ReadLine();
        }
        static void MutiplyByTwo(double a)
        {
            Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
        }
        static void Square(double a)
        {
            Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
        }
    }

上面是最基本的使用多播委托的方法,但是它存在以下問題

a、對同一個委托調用方法鏈的順序並未正式定義,因此應避免編寫依賴於特定順序的調用方法的代碼。

b、通過一個委托調用多個方法還可能導致大問題,當一個方法拋出異常,整個叠代就會停止

b的實現代碼如下:

    class Program
    {
        static void Main(string[] args)
        {
            Action<double> options = new Action<double>(MutiplyByTwo);
            options += Square;
            options.Invoke(3);
            Console.ReadLine();
        }
        static void MutiplyByTwo(double a)
        {
            Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
            throw new Exception("我出異常啦");
        }
        static void Square(double a)
        {
            Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
        }
    }

技術分享

第二行代碼並沒有正常輸出,ok,那麽將異常捕獲,看它會不會執行第二行代碼,代碼如下:

    class Program
    {
        static void Main(string[] args)
        {
            Action<double> options = new Action<double>(MutiplyByTwo);
            options += Square;
            try
            {
                options.Invoke(3);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception Caught");
            }
            Console.ReadLine();
        }
        static void MutiplyByTwo(double a)
        {
            Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
            throw new Exception("我出異常啦");
        }
        static void Square(double a)
        {
            Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
        }
    }

技術分享

異常被捕獲,但是第二行代碼還是沒有執行,這是因為第一個方法拋出了異常,所以委托的叠代會停止,不再調用下面的方法。

b的解決方法:

為了解決上面的問題,Delegate類定義了GetInvocationList方法,他返回一個Delegate[]數組。現在可以使用對象吊用委托對應的方法,捕獲異常,並進行下一次叠代,代碼如下:

class Program
    {
        static void Main(string[] args)
        {
            Action<double> options = new Action<double>(MutiplyByTwo);
            options += Square;
            Delegate[] delegates = options.GetInvocationList();
            foreach (Action<double> a in delegates)
            {
                try
                {
                    a.Invoke(3);
                }
                catch (Exception)
                {
                    Console.WriteLine("Exception Caught");
                }
            }
            Console.ReadLine();
        }
        static void MutiplyByTwo(double a)
        {
            Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
            throw new Exception("我出異常啦");
        }
        static void Square(double a)
        {
            Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
        }
    }

技術分享

ok,問題解決,異常順利的捕獲,且委托的叠代正常完成!

C# 委托基礎