久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

.Net委托類型解析

 nisz 2016-06-25

不像Windows API中使用C語言風(fēng)格的函數(shù)指針這種不安全的方式進行回調(diào)。.Net中此功能使用使用更為安全和面向?qū)ο蟮奈?delegate)來完成,。委托是一個類型安全的對象,它指向程序中另一個以后會被調(diào)用的方法(或多個方法),。

委托類型包含3個重要信息:

  • 它所調(diào)用的方法的名稱
  • 該方法的參數(shù)(可選)
  • 該方法的返回值(可選)

當上述信息被提供后,,委托可以在運行時動態(tài)調(diào)用其指向的方法。很重要的一點:.Net中每個委托都被自動賦予同步或異步訪問方法的能力,。

定義委托

在C#中使用delegate關(guān)鍵字創(chuàng)建一個委托,。我們稱這種類為委托類。委托類的實例成為委托對象,。從概念上說,,委托對象是一種指向一個或多個方法(靜態(tài)或非靜態(tài))的引用。要求是此委托匹配它指向的方法的簽名,。

如下委托可以指向一任何傳入兩個整數(shù)返回一個整數(shù)的方法,。

C#
1
public delegate int BinaryOp(int x, int y);
  • 定義委托后,系統(tǒng)生成一個派生自MulticastDelegate類的密封類,。此類中有3個方法:
  • Invoke()方法,,用來以同步方式調(diào)用委托維護的每個方法。(不能在C#中顯示調(diào)用此方法,,Invoke()在后臺被調(diào)用)
  • BeginInvoke()與EndInvoke()方法在第二個線程上異步調(diào)用當前方法,。

開發(fā)人員創(chuàng)建第二個執(zhí)行線程的原因調(diào)用比較耗時的方法。(相當于委托順帶實現(xiàn)了一些System.Threading命名空間管理的線程問題)

這個委托的密封類大概如下:

C#
1
2
3
4
5
6
7
sealed class BinaryOp : System.MulticastDelegate
{
      public BinaryOp(object target, uint functionAddress);
      public int Invoke(int x, int y);
      public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state);
      public int EndInvoke(IAsyncResult result);
}

下面給一個簡單的委托示例:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  // 這個委托指向任何一個傳入兩個整數(shù)并返回一個整數(shù)的方法
    public delegate int BinaryOp(int x, int y);
    #region SimpleMath class
    public class SimpleMath
    {
        public int Add(int x, int y)
        { return x + y; }
        public int Subtract(int x, int y)
        { return x - y; }
        public static int SquareNumber(int a)
        { return a * a; }
    }
    #endregion
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine('***** Simple Delegate Example *****n');
            // 創(chuàng)建一個指向SimpleMath.Add()方法的BinaryOp對象
            SimpleMath m = new SimpleMath();
            BinaryOp b = new BinaryOp(m.Add);
            // 使用委托調(diào)用調(diào)用Add()方法
            // 此處也是Invoke()被調(diào)用的位置
            Console.WriteLine('n10 + 10 is {0}', b(10, 10));
            Console.ReadLine();
        }
    }

委托類型安全的體現(xiàn)

如果傳入一個與委托聲明不匹配的方法,,將在編譯時報錯,。如上例中如果傳入int SquareNumber(int),,將會導(dǎo)致一個編譯時錯誤。

獲取委托中調(diào)用函數(shù)列表的方法,,示例:

假設(shè)有名為delObj的委托對象,使用如下方式得到調(diào)用函數(shù)的信息

C#
1
2
3
 Delegate d in delObj.GetInvocationList()
    Console.WriteLine('Method Name: {0}', d.Method);
    Console.WriteLine('Target Name: {0}', d.Target);

Method屬性表示調(diào)用的函數(shù)的簽名,,Target表示調(diào)用的函數(shù)所在的對象的類型名,,所以如果委托調(diào)用的是一個靜態(tài)方法則Target不會有任何顯示,只有當委托調(diào)用的是一個實例方法時,,Target屬性才有值,。

更完整的委托應(yīng)用(示例來自C#與.Net3.0高級程序設(shè)計),代碼:

汽車類:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
using System;
using System.Collections.Generic;
using System.Text;
namespace CarDelegate
{
    public class Car
    {
        // 定義委托類型
        public delegate void AboutToBlow(string msg);
        public delegate void Exploded (string msg);
        // 定義各自委托類型的對象
        private AboutToBlow almostDeadList;
        private Exploded explodedList;
        // 將成員添加到調(diào)用列表
        public void OnAboutToBlow(AboutToBlow clientMethod)
        { almostDeadList += clientMethod; }
        public void OnExploded(Exploded clientMethod)
        { explodedList += clientMethod; }
        // 由調(diào)用列表移除方法
        public void RemoveAboutToBlow(AboutToBlow clientMethod)
        { almostDeadList -= clientMethod; }
        public void RemoveExploded(Exploded clientMethod)
        { explodedList -= clientMethod; }
        // 內(nèi)部狀態(tài)成員
        private int currSpeed;
        private int maxSpeed;
        private string petName;
        // 汽車壞了嗎,?
        bool carIsDead;
        public Car()
        {
            maxSpeed = 100;
        }
        public Car(string name, int max, int curr)
        {
            currSpeed = curr;
            maxSpeed = max;
            petName = name;
        }
        public void SpeedUp(int delta)
        {
            // 如果汽車壞了,,觸發(fā)Exploded事件
            if (carIsDead)
            {
                if (explodedList != null)
                    explodedList('Sorry, this car is dead');
            }
            else
            {
                currSpeed += delta;
                // 幾乎要壞了?
                if (10 == maxSpeed - currSpeed
                    && almostDeadList != null)
                {
                    almostDeadList('Careful buddy!  Gonna blow!');
                }
                // 還好,!
                if (currSpeed >= maxSpeed)
                    carIsDead = true;
                else
                    Console.WriteLine('->CurrSpeed = {0}', currSpeed);
            }
        }
    }
}

主函數(shù):

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
namespace CarDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            // 制造一輛車
            Car c1 = new Car('SlugBug', 100, 10);
            // 注冊事件處理函數(shù)          
            Car.Exploded d = new Car.Exploded(CarExploded);
            c1.OnAboutToBlow(new Car.AboutToBlow(CarIsAlmostDoomed));
            c1.OnAboutToBlow(new Car.AboutToBlow(CarAboutToBlow));
            c1.OnExploded(d);
            // 加速 (這將觸發(fā)事件)
            Console.WriteLine('n***** 加速 *****');
            for (int i = 0; i < 6; i++)
                c1.SpeedUp(20);
            // 由調(diào)用列表移除CarExploded方法
            c1.RemoveExploded(d);
            Console.WriteLine('n***** 加速 *****');
            for (int i = 0; i < 6; i++)
                c1.SpeedUp(20);
            Console.ReadLine();
        }
        public static void CarAboutToBlow(string msg)
        { Console.WriteLine(msg); }
        public static void CarIsAlmostDoomed(string msg)
        { Console.WriteLine('Critical Message from Car: {0}', msg); }
        public static void CarExploded(string msg)
        { Console.WriteLine(msg); }
    }
}

對多路廣播的支持

.Net委托內(nèi)置多路,,即一個委托可以維護一個可調(diào)用方法的列表而不只是單獨一個方法,使用重載過的+=運算符可以向一個委托對象添加多個方法,。關(guān)于對對路廣播的支持可以參考上述示例,。

在多路廣播的支持中有一個需要注意的問題,一個委托調(diào)用的多個方法需要無參數(shù)且無返回值,,因為在調(diào)用委托時,,即使傳入了參數(shù)也不知道具體應(yīng)該傳給哪一個方法,即使這些方法有返回值也不知道該接受那個函數(shù)的返回值,。所以說直接不要調(diào)用有參數(shù)及返回值的方法,,這點與事件關(guān)聯(lián)多個事件處理方法時對處理方法簽名的要求相同(可以參見本系列介紹事件的文章)。

注意:我們可以用調(diào)用方法的語法”調(diào)用”委托對象,。這樣會調(diào)用委托對象所引用的方法,。(事件的觸發(fā)與委托的調(diào)用相同,本來事件就是一個委托類型的對象),。這些方法的調(diào)用是在調(diào)用委托的方法所在的線程中完成的,。這種調(diào)用稱同步調(diào)用。

C#2.0編譯器的委托類推測功能

C#編譯器引入了在創(chuàng)建委托變量時可以推測其類型的能力,。這樣就可以將一個方法賦給隱式創(chuàng)建的委托對象,。

示例:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Program {
  delegate void Deleg1();
  delegate string Deleg2( string s );
  static void f1() {
    System.Console.WriteLine('f1() called.');
  }
  static string f2(string s) {
    string _s=string.Format( 'f2() called with the param '{0}'.' , s );
    System.Console.WriteLine( _s );
    return _s;
  }
  public static void Main() {
     Deleg1 d1 = f1; // 代替 Deleg1 d1 = new Deleg1( f1 );
     d1();
     Deleg2 d2 = f2; // 代替 Deleg2 d2 = new Deleg2( f2 );
     string s = d2('hello');
  }
}

委托協(xié)變(covariance)

允許創(chuàng)建一個委托,其返回的對象的類型是繼承關(guān)系的,,示例代碼:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 簡單的集成關(guān)系的兩個類
    class Car
    {
        public override string ToString()
        {
            return 'A stateless car';
        }
    }
    class SportsCar : Car
    {
        public override string ToString()
        {
            return 'A stateless sports car';
        }
    } 
    class Program
    {
        // 定義一個返回Car或SportsCar的委托
        public delegate Car ObtainVehicalDelegate();
        // 委托指向目標
        public static Car GetBasicCar()
        { return new Car(); }
        public static SportsCar GetSportsCar()
        { return new SportsCar(); }
        static void Main(string[] args)
        {
            ObtainVehicalDelegate targetA = new ObtainVehicalDelegate(GetBasicCar);
            Car c = targetA();
            Console.WriteLine(c);
            // 協(xié)變允許指定這樣的目標方法
            ObtainVehicalDelegate targetB = new ObtainVehicalDelegate(GetSportsCar);
            SportsCar sc = (SportsCar)targetB();
            Console.WriteLine(sc);
            Console.ReadLine();
        }
    }

委托逆變,,其中參數(shù)具有集成關(guān)系,委托簽名的參數(shù)類型(派生類型)比方法具有的參數(shù)類型(基類型)更具體,。定義一個參數(shù)類型是派生類型的委托,,這個委托可以接收具有基類型參數(shù)的方法,因為派生類型隱式轉(zhuǎn)換成了基類型。注意此方法必須接收與委托簽名相同的參數(shù)類型(派生類型),,雖然方法的簽名中參數(shù)是基類型,。

接下來說一下委托在多線程程序中的應(yīng)用,主角有兩個:ThreadStart和ParameterizedThreadStart,,它們都定義與System.Threading命名空間下,。

使用這兩個委托,你可以以編程方式創(chuàng)建此線程來分擔一些任務(wù),,步驟如下:

  1. 創(chuàng)建一個方法作為新線程的入口點,。
  2. 創(chuàng)建一個ParameterizedThreadStart(或ThreadStart)委托,并把之前所定義的方法傳給委托的構(gòu)造函數(shù),。
  3. 創(chuàng)建一個Thread對象,,并把ParameterizedThreadStart或ThreadStart委托作為構(gòu)造函數(shù)的參數(shù)。
  4. 建立任意初始化線程的特性(名稱,、優(yōu)先級等),。
  5. 調(diào)用Thread.Start()方法。

完成上述步驟,,在第2步建立的委托所指向的方法將在線程中盡快開始執(zhí)行,。

ThreadStart委托指向一個沒有參數(shù)、無返回值的方法,,它在調(diào)用一個被設(shè)計用來僅僅在后臺運行,、而沒有更多的交互時非常有用。它的局限在于無法給這個函數(shù)出入?yún)?shù),,所以在.Net2.0中出現(xiàn)了ParameterizedThreadStart了方法,,它可以接受一個包含了任意個數(shù)的參數(shù)(傳給它要調(diào)用的方法的)的Object類型對象做參數(shù)(即允許用戶為新線程要執(zhí)行的方法傳入一個對象作為參數(shù))。但注意這兩種委托指向的函數(shù)的返回值都必須是void,。

看看示例代碼,,首先是ThreadStart委托的:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Printer
    {
        public void PrintNumbers()
        {
            //具體實現(xiàn)省略
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Printer p = new Printer();
            Thread bgroundThread = new Thread(new ThreadStart(p.PrintNumbers));
            // 控制此線程是否在后臺運行?
            bgroundThread.IsBackground = true;
            bgroundThread.Start();
        }
}

接下來的代碼示例了ParameterizedThreadStart委托的使用:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    //包裝參數(shù)的類
    class AddParams
    {
        public int a;
        public int b;
        public AddParams(int numb1, int numb2)
        {
            a = numb1;
            b = numb2;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine('主線程ID:{0}', Thread.CurrentThread.GetHashCode());
            //生成要傳入的參數(shù)
            AddParams ap = new AddParams(10, 10);
            Thread t = new Thread(new ParameterizedThreadStart(Add));
            t.Start(ap);
        }
        //ParameterizedThreadStart委托調(diào)用的方法
        //使用AddParams類對象做參數(shù)
        public static void Add(object data)
        {
            if (data is AddParams)
            {
                Console.WriteLine('后臺線程ID: {0}', Thread.CurrentThread.GetHashCode());
                AddParams ap = (AddParams)data;
                Console.WriteLine('{0} + {1} is {2}', ap.a, ap.b, ap.a + ap.b);
            }
        }
}

詳細通過上面兩段簡單的代碼示例,,你已經(jīng)對ThreadStart和ParameterizedThreadStart的使用有了全面的了解,。

另外有一點需要說的,有些情況下可以省略這個委托對象的構(gòu)造,,即構(gòu)造Thread對象時,,直接向Thread的構(gòu)造函數(shù)傳入一個方法的名稱,而不用先構(gòu)造一個委托的對象,。傳入的方法既可以是靜態(tài)方法也可以是實例方法,。

    另外委托在異步編程中的作用見異步編程的文章

參考資料:

C#與.Net3.0高級程序設(shè)計

C#與.Net2.0實戰(zhàn)

CLR via C# 第二版

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多