zoukankan      html  css  js  c++  java
  • C#6.0语言规范(十五) 委托

    委托启用其他语言(如C ++,Pascal和Modula)已使用函数指针进行寻址的方案。但是,与C ++函数指针不同,委托是完全面向对象的,与成员函数的C ++指针不同,委托封装了对象实例和方法。

    委托声明定义了从类派生的类System.Delegate委托实例封装了一个调用列表,该列表是一个或多个方法的列表,每个方法都被称为可调用实体。对于实例方法,可调用实体由该实例上的实例和方法组成。对于静态方法,可调用实体仅包含一个方法。使用适当的参数集调用委托实例会导致使用给定的参数集调用每个委托的可调用实体。

    委托实例的一个有趣且有用的属性是它不知道或不关心它封装的方法的类; 重要的是这些方法与委托的类型兼容(委托声明)。这使得委托完全适合“匿名”调用。

    委托声明

    一个delegate_declarationtype_declaration类型声明,声明一个新的委托类型)。

     1 delegate_declaration
     2     : attributes? delegate_modifier* 'delegate' return_type
     3       identifier variant_type_parameter_list?
     4       '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
     5     ;
     6 
     7 delegate_modifier
     8     : 'new'
     9     | 'public'
    10     | 'protected'
    11     | 'internal'
    12     | 'private'
    13     | delegate_modifier_unsafe
    14     ;

    同一修饰符在委托声明中多次出现是编译时错误。

    所述new改性剂只允许在另一种类型的,在这种情况下,它指明这种委托隐藏同名一个继承的成员,如在描述中声明委托新的改性剂

    publicprotectedinternal,和private修饰符控制委托类型的可访问性。根据委托声明发生的上下文,可能不允许某些修饰符(声明的可访问性)。

    委托的类型名称是标识符

    可选的formal_parameter_list指定委托的参数,return_type指示委托的返回类型。

    可选的variant_type_parameter_listVariant类型参数列表)指定委托本身的类型参数。

    委托类型的返回类型必须是void或输出安全(方差安全)。

    委托类型的所有形式参数类型必须是输入安全的。此外,任何outref参数类型也必须是输出安全的。请注意out,由于底层执行平台的限制,甚至参数都需要输入安全。

    C#中的委托类型是名称等价的,在结构上不等同。具体而言,具有相同参数列表和返回类型的两种不同委托类型被视为不同的委托类型。但是,两个不同但结构上等效的委托类型的实例可以比较为相等(委托相等运算符)。

     1 delegate int D1(int i, double d);
     2 
     3 class A
     4 {
     5     public static int M1(int a, double b) {...}
     6 }
     7 
     8 class B
     9 {
    10     delegate int D2(int c, double d);
    11     public static int M1(int f, double g) {...}
    12     public static void M2(int k, double l) {...}
    13     public static int M3(int g) {...}
    14     public static void M4(int g) {...}
    15 }

    这些方法A.M1B.M1与双方委托类型兼容D1D2,因为它们具有相同的返回类型和参数列表; 但是,这些委托类型是两种不同的类型,因此它们不可互换。的方法B.M2B.M3以及B.M4与委托类型不兼容D1D2,因为他们有不同的返回类型或参数列表。

    与其他泛型类型声明一样,必须提供类型参数以创建构造的委托类型。通过为委托声明中的每个类型参数替换构造的委托类型的相应类型参数,来创建构造的委托类型的参数类型和返回类型。生成的返回类型和参数类型用于确定哪些方法与构造的委托类型兼容。例如:

    1 delegate bool Predicate<T>(T value);
    2 
    3 class X
    4 {
    5     static bool F(int i) {...}
    6     static bool G(string s) {...}
    7 }

    该方法X.F与委托类型兼容,Predicate<int>并且该方法X.G与委托类型兼容Predicate<string>

    声明委托类型的唯一方法是通过delegate_declaration委托类型是派生自的类类型System.Delegate委托类型是隐式的sealed,因此不允许从委托类型派生任何类型。也不允许从中派生非委托类类型System.Delegate请注意,System.Delegate它本身不是委托类型; 它是一个类类型,从中派生所有委托类型。

    C#为委托实例化和调用提供了特殊语法。除了实例化之外,任何可以应用于类或类实例的操作也可以分别应用于委托类或实例。特别是,可以System.Delegate通过通常的成员访问语法访问类型的成员。

    由委托实例封装的方法集称为调用列表。从单个方法创建委托实例(委托兼容性)时,它封装该方法,并且其调用列表仅包含一个条目。但是,当组合两个非null委托实例时,它们的调用列表将按照左操作数和右操作数的顺序连接,以形成一个新的调用列表,其中包含两个或多个条目。

    使用二进制+加法运算符)和+=运算符(复合赋值组合委托可以使用二进制-减法运算符)和-=运算符(复合赋值从委托组合中删除委托可以比较委托的相等性(委托相等运算符)。

    以下示例显示了许多委托的实例化及其相应的调用列表:

     1 delegate void D(int x);
     2 
     3 class C
     4 {
     5     public static void M1(int i) {...}
     6     public static void M2(int i) {...}
     7 
     8 }
     9 
    10 class Test
    11 {
    12     static void Main() {
    13         D cd1 = new D(C.M1);      // M1
    14         D cd2 = new D(C.M2);      // M2
    15         D cd3 = cd1 + cd2;        // M1 + M2
    16         D cd4 = cd3 + cd1;        // M1 + M2 + M1
    17         D cd5 = cd4 + cd3;        // M1 + M2 + M1 + M1 + M2
    18     }
    19 
    20 }

    cd1cd2实例化时,它们都封装了一个方法。cd3实例化时,它具有两个方法的调用列表,M1并按M2顺序。cd4的调用列表包含M1,, M2M1,按顺序。最后cd5的调用列表中包含M1M2M1M1,并M2按此顺序。有关组合(以及删除)委托的更多示例,请参阅委派调用

    委派兼容性

    的方法或委托M兼容与委托类型D如果以下所有条件都为真:

    • D并且M具有相同数量的参数,并且每个参数与相应的参数D具有相同refout修饰符M
    • 对于每个值参数(带有no refoutmodifier 的参数),从参数类型到相应的参数类型中存在标识转换(标识转换)或隐式引用转换(隐式引用转换DM
    • 对于每个refout参数,参数类型in与参数类型D相同M
    • 从返回类型M到返回类型的存在标识或隐式引用转换D

    委托实例化

    委托的实例由delegate_creation_expression委托创建表达式)或转换为委托类型创建。新创建的委托实例然后引用:

    • delegate_creation_expression中引用的静态方法,或
    • nulldelegate_creation_expression中引用的目标对象(不可以)和实例方法,或
    • 另一位委托。

    例如:

     1 delegate void D(int x);
     2 
     3 class C
     4 {
     5     public static void M1(int i) {...}
     6     public void M2(int i) {...}
     7 }
     8 
     9 class Test
    10 {
    11     static void Main() { 
    12         D cd1 = new D(C.M1);        // static method
    13         C t = new C();
    14         D cd2 = new D(t.M2);        // instance method
    15         D cd3 = new D(cd2);        // another delegate
    16     }
    17 }

    实例化后,委托实例始终引用相同的目标对象和方法。请记住,当两个委托组合在一起,或者一个委托从另一个委托中删除时,新的委托会产生自己的调用列表; 组合或删除的委托的调用列表保持不变。

    委托调用

    C#提供了调用委托的特殊语法。当调用其调用列表包含一个条目的非null委托实例时,它将调用具有相同参数的one方法,并返回与引用方法相同的值。(有关委托调用的详细信息,请参阅委派调用。)如果在调用此类委托期间发生异常,并且该异常未在调用的方法中捕获,则在调用的方法中继续搜索异常catch子句委托,就好像该方法直接调用了该委托引用的方法一样。

    通过按顺序同步调用调用列表中的每个方法来调用其调用列表包含多个条目的委托实例。所谓的每个方法都传递给委托实例的同一组参数。如果这样的委托调用包含引用参数(引用参数),则每个方法调用都将引用同一个变量; 通过调用列表中的一个方法对该变量的更改将对调用列表中的下一个方法可见。如果委托调用包括输出参数或返回值,则它们的最终值将来自列表中最后一个委托的调用。

    如果在处理调用此类委托期间发生异常,并且该异常未在调用的方法中捕获,则在调用委托的方法中继续搜索异常catch子句,并在调用之后继续执行任何方法列表未被调用。

    尝试调用值为null的委托实例会导致类型异常System.NullReferenceException

    以下示例显示如何实例化,组合,删除和调用委托:

     1 using System;
     2 
     3 delegate void D(int x);
     4 
     5 class C
     6 {
     7     public static void M1(int i) {
     8         Console.WriteLine("C.M1: " + i);
     9     }
    10 
    11     public static void M2(int i) {
    12         Console.WriteLine("C.M2: " + i);
    13     }
    14 
    15     public void M3(int i) {
    16         Console.WriteLine("C.M3: " + i);
    17     }
    18 }
    19 
    20 class Test
    21 {
    22     static void Main() { 
    23         D cd1 = new D(C.M1);
    24         cd1(-1);                // call M1
    25 
    26         D cd2 = new D(C.M2);
    27         cd2(-2);                // call M2
    28 
    29         D cd3 = cd1 + cd2;
    30         cd3(10);                // call M1 then M2
    31 
    32         cd3 += cd1;
    33         cd3(20);                // call M1, M2, then M1
    34 
    35         C c = new C();
    36         D cd4 = new D(c.M3);
    37         cd3 += cd4;
    38         cd3(30);                // call M1, M2, M1, then M3
    39 
    40         cd3 -= cd1;             // remove last M1
    41         cd3(40);                // call M1, M2, then M3
    42 
    43         cd3 -= cd4;
    44         cd3(50);                // call M1 then M2
    45 
    46         cd3 -= cd2;
    47         cd3(60);                // call M1
    48 
    49         cd3 -= cd2;             // impossible removal is benign
    50         cd3(60);                // call M1
    51 
    52         cd3 -= cd1;             // invocation list is empty so cd3 is null
    53 
    54         cd3(70);                // System.NullReferenceException thrown
    55 
    56         cd3 -= cd1;             // impossible removal is benign
    57     }
    58 }

    如语句所示cd3 += cd1;,委托可以多次出现在调用列表中。在这种情况下,每次出现只需调用一次。在诸如此类的调用列表中,当删除该委托时,调用列表中的最后一个实例是实际删除的那个。

    在执行最终语句之前,cd3 -= cd1;委托cd3引用空的调用列表。尝试从空列表中删除委托(或从非空列表中删除不存在的委托)不是错误。

    产生的输出是:

     1 C.M1: -1
     2 C.M2: -2
     3 C.M1: 10
     4 C.M2: 10
     5 C.M1: 20
     6 C.M2: 20
     7 C.M1: 20
     8 C.M1: 30
     9 C.M2: 30
    10 C.M1: 30
    11 C.M3: 30
    12 C.M1: 40
    13 C.M2: 40
    14 C.M3: 40
    15 C.M1: 50
    16 C.M2: 50
    17 C.M1: 60
    18 C.M1: 60
  • 相关阅读:
    棋盘完美覆盖数(小规模原理实现)
    Codeforces 115A Party (并查集思维)
    datetime日期和时间
    range与enumerate的区别
    爬取爱套图网上的图片
    python爬取365好书中小说
    列表和元组的方法
    字符串中的方法
    从电源问题出发,带你揭秘新体系结构范式 COA
    KubeCon 2020 演讲集锦|《阿里巴巴云原生技术与实践 13 讲》开放下载
  • 原文地址:https://www.cnblogs.com/strengthen/p/9742932.html
Copyright © 2011-2022 走看看