委托 订阅
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。 展开全文
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
信息
外文名
delegate
定    义
方法的类型
作    用
程序具有更好的可扩展性
中文名
委托
解    释
用来执行方法(函数)的一个东西
委托引言
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
收起全文
精华内容
下载资源
问答
  • 委托

    千次阅读 多人点赞 2012-02-18 22:45:36
    委托的概念 委托实际上是类(一个貌似函数一样的类),我们已经使用函数指针很多年了——函数指针也被称为过程类型,但是它们的实现都不是类。它们是单独的函数指针的简单实例。委托是包含这些功能的类,委托类通过...

    委托的概念

    委托实际上是类(一个貌似函数一样的类),我们已经使用函数指针很多年了——函数指针也被称为过程类型,但是它们的实现都不是类。它们是单独的函数指针的简单实例。委托是包含这些功能的类,委托类通过保留的列表来确定某些事物是否已经指派给了委托,这种算法可以理解为:“对于内部列表中的每一个回调函数,都调用函数”。委托除了支持回调函数外,还可以通过该列表来实现多播(委托链)。

    说白了,就是我们把一批具有相同特征的方法,通过建立与具有同样相同特征的委托的一个实例来进行传递,以使其它地方能对这些方法进行调用。即把方法当作参数进行传递(浅显的认识,勿笑)。

     

    初识委托

    示例1:

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Web;

     

     

    namespace MyDelegateTest

    {

         /// <summary>

         /// 说明:一个简单的委托示例

         /// 作者:文野

         /// 联系:stwyhm.cnblogs.com

         /// </summary>

     

         // 新建一个用以输出消息的委托

         public delegate void WriteMessage();

        

         public class WriteToWeb

         {

             // 一个输出消息的静态方法

             public static void StaticWrite()

             {

                  HttpContext.Current.Response.Write("委托调用了一个静态方法。<br />");

             }

     

             // 一个输出消息的类实例方法

             public void Write()

             {

                  HttpContext.Current.Response.Write("委托调用了一个类实例方法。<br />");

             }

         }

    }

    调用:

    protected void Page_Load(object sender, EventArgs e)

        {

             // 新建一个委托,回调函数是一个静态方法

             WriteMessage wm = new WriteMessage(WriteToWeb.StaticWrite);

     

             WriteToWeb w = new WriteToWeb();

             // 新建一个委托并与加到先前建立的委托组成委托链,回调函数是一个类实例方法

             wm += new WriteMessage(w.Write);

     

             // 执行委托链上所有的回调函数

             wm();

        }

     

    上面的示例程序首先建立了一个简单得不能再简单的用以输出的委托,下面的类中有两个与委托签名一致的方法(一个静态方法,一个类实例方法)。下面的调用过程首先建立了一个使用回调静态方法的委托,其后又建立了一个使用类实例方法的委托并与先前的委托组成了一个委托链,最后执行委托链上所有的方法。

    由此可见,委托最简单的理解就是利用与方法签名一致的委托,可以把方法当作参数一样来传递,无论是静态方法还是类实例方法。

     

    委托的秘密


    从上面的类视图中我们看到,对委托的定义最终被编译成一个类,这个类中定义有4种方法:构造器,Invoke,BeginInvoke,EndInvoke。

    所有的委托都继承自MulticastDelegate,而MulticastDelegate又继续至Delegate。这样我们定义的委托自然也就继承了MulticastDelegate的字段、属性和方法。在继承得到的所有成员中,有3个最重要的字段:

    1、_target:指向调用回调函数时应该操作的对象。该字段用于实例方法的回调。

    2、_methodPtr:一个内部的整数值,CLR用它来识别回调的方法。

    3、_prev:指向另一个委托对象。

    当编译器知道我们在构造的是一个委托时,它会分析源代码来确定要引用哪个对象和方法。其中对象引用会被传递给_target(对于静态方法,_target被置为null),一个特殊的标识方法的Int32值会被传递给_methodPtr,_prev在构造器中被置为null,它被用于在委托链中记录下一个委托的引用。

    每个委托对象实际上是对方法及其调用操作的一个包装,MulticastDelegate中定义了两个只读属性,Target和Method,Target(其实就是前面的_target字段)属性返回一个方法回调时操作的对象的引用,Method属性返回一个标识回调方法的System.Reflection.MethodInfo对象。

     

    委托链

    前面介绍过MulticastDelegate中有一个_prev的私有字段,这个字段指向另一个MulticastDelegate对象的引用,这样就实现了委托链(其实与我们在学链表时的实现方式是一致的)。

    当委托链表被调用时,它首先会调用委托中在其前面的委托对象,这里如果被调的回调方法具有返回值,将被丢失,委托链只会返回最后一次调用回调方法的返回值。

     

    委托示例

    这是一个我自认为比较经典的委托示例(给排序算法传递一个动态比较的函数)。

     

    using System;

    using System.Collections.Generic;

    using System.Text;

     

    namespace MyDelegateTest

    {

         /// <summary>

         /// 说明:给排序算法传递一个动态比较函数的委托示例

         /// 作者:文野

         /// 联系:stwyhm.cnblogs.com

         /// </summary>

        

         // 进行排序的委托

         public delegate bool Compare(int left, int right);

     

         public class DelegateSample

         {

             private int[] items;

     

             public int[] Items

             {

                  set { items = value; }

                  get { return items; }

             }

     

             // 比大

             public bool Greater(int left, int right)

             {

                  return left > right;

             }

     

             // 比小

             public bool Less(int left, int right)

             {

                  return !Greater(left, right);

             }

     

             public void Sort(Compare compare)

             {

                  for (int i = 0; i < items.Length-1; i++)

                  {

                       for (int j = i + 1; j < items.Length; j++)

                       {

                           if (compare(items[i], items[j]))

                           {

                                int tmp = items[i];

                                items[i] = items[j];

                                items[j] = tmp;

                           }

                       }

                  }

             }

         }

    }

     

    调用页面

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

     

    using MyDelegateTest;

     

    public partial class Sort : System.Web.UI.Page

    {

         protected void Page_Load(object sender, EventArgs e)

         {

     

         }

         protected void Button1_Click(object sender, EventArgs e)

         {

             DelegateSample sample = new DelegateSample();

             sample.Items = GetItems();

     

             // 使用降序

             sample.Sort(new Compare(sample.Less));

     

             PrintItems(sample);

         }

     

         private void PrintItems(DelegateSample sample)

         {

             for (int i = 0; i < sample.Items.Length; i++)

             {

                  Response.Write(sample.Items[i] + "<br/>");

             }

         }

     

         private int[] GetItems()

         {

             string[] str = this.TextBox1.Text.Split(",".ToCharArray());

             int[] items = new int[str.Length];

             for (int i = 0; i < str.Length; i++)

                  items[i] = int.Parse(str[i]);

     

             return items;

         }

         protected void Button2_Click(object sender, EventArgs e)

         {

             DelegateSample sample = new DelegateSample();

             sample.Items = GetItems();

     

             // 使用升序

             sample.Sort(new Compare(sample.Greater));

     

             PrintItems(sample);

         }

    }

     

    效果图:

    降序



    升序


     

    参考资料:

    《.NET框架程序设计》

    《Visual Basic .Net Power Coding》

    展开全文
  • /// <summary> /// 学生类 /// </summary> public class Student { public int Id { get; set; }
  • 委托

    千次阅读 2006-12-17 09:20:00
    委托是C#中的一种引用类型,类似于C/C++中的函数指针。与函数指针不同的是,委托是面向对象、类型安全的,而且委托可以引用静态方法和实例方法,而函数指针只能引用静态函数。委托主要用于 .NET Framework 中的事件...
     委托是C#中的一种引用类型,类似于C/C++中的函数指针。与函数指针不同的是,委托是面向对象、类型安全的,而且委托可以引用静态方法和实例方法,而函数指针只能引用静态函数。委托主要用于 .NET Framework 中的事件处理程序和回调函数。 
    
           一个委托可以看作一个特殊的类,因而它的定义可以像常规类一样放在同样的位置。与其他类一样,委托必须先定义以后,再实例化。与类不同的是,实例化的委托没有与之相应的术语(类的实例化称作对象),作为区分我们将实例化的委托称为委托实例。
     一个函数在编译时被分配给一个入口地址,这个入口地址就称为函数的指针,正如同指针是一个变量的地址一样。
    函数指针的用途很多,最常用的用途之一是把指针作为参数传递到其他函数。我们可以参考下面的例子进一步理解函数指针作为参数的情况:
    客观的讲,使用函数指针作为其参数的函数如果直接调用函数或是直接把调用的函数的函数体放在这个主函数中也可以实现其功能。那么为什么还要使用函数指针呢?我们仔细看一下上面的main()函数就可以发现,main()函数两次调用了test函数,前一次求出最大最小值,后一次求出两数的和与差。如果我们test函数不用函数指针,而是采用直接在test函数中调用函数的方法,使用一个test函数还能完成这个功能吗?显然不行,我们必须写两个这样的test函数供main()函数调用,虽然大多数代码还是一样的,仅仅是调用的函数名不一样。上面仅仅是一个简单的例子,实际生活中也许main()函数会频繁的调用test(),而每次的差别仅仅是完成的功能不一样,也许第一次调用会要求求出两数的和与差,而下一次会要求求出最大值以及两数之和,第三次呢,也许是最小值和最大值,……,如果不用函数指针,我们需要写多少个这样的test()函数?显然,函数指针为我们的编程提供了灵活性。
    另外,有些地方必须使用到函数指针才能完成给定的任务,特别是异步操作的回调和其他需要匿名回调的结构。另外,像线程的执行,事件的处理,如果缺少了函数指针的支持也是很难完成的。
    类型安全
    从上面的介绍可以看出,函数指针的提出还是有其必要的,上面的介绍也同时说明了委托存在的必要性。那么为什么C#中不直接用函数指针,而是要使用委托呢?这就涉及到另外一个问题:C#是类型安全的语言。何谓类型安全?这里的类型安全特指内存类型安全,即类型安全代码只访问被授权可以访问的内存位置。如果代码以任意偏移量访问内存,该偏移量超出了属于该对象的公开字段的内存范围,则它就不是类型安全的代码。显然指针不属于类型安全代码,这也是为什么C#使用指针时必须申明unsafe的缘故。
    那么类型不安全代码可能会带来什么不良的后果呢?相信对于安全技术感兴趣的朋友一定十分熟悉缓冲区溢出问题,通过缓冲区溢出攻击者可以运行非法的程序获得一定的权限从而攻击系统或是直接运行恶意代码危害系统,在UNIX下这是一个十分普遍的问题。那么缓冲区溢出又和函数指针有什么关系呢?事实上,攻击者就是通过缓冲区溢出改变返回地址的值到恶意代码地址来执行恶意代码的。我们可以看看下面的代码:
    void copy()
                {
                     char buffer[128];
                     ........
                         strcpy (buffer,getenv("HOME"));//HOME为UNIX系统中的HOME环境变量
                     ........
                }
    上面的代码中如果HOME环境变量的字符数大于128,就会产生缓冲区溢出,假如这个缓冲区之前有另一个函数的返回地址,那么这一是地址就有可能覆盖,而覆盖这一地址的字符有可能就是恶意代码的地址,攻击者就有可能攻击成功了!
    上面的例子仅仅是指针问题中的一种,除此以外,还可能由于错误的管理地址,将数据写入错误地址,造成程序的崩溃;还可能由于对指针不恰当的赋值操作产生悬浮指针;还可能产生内存越界,内存泄漏等等问题。
    由此可见,指针不是类型安全的,函数指针当然也不例外,所以C#里面没有使用函数指针,而且不建议使用指针变量。
    委托
    前面的说明充分证明了委托存在的必要性,那么我们再谈谈为什么委托是类型安全的。C#中的委托和指针不一样,指针不通过MSIL而是直接和内存打交道,这也是指针不安全的原因所在,当然也是采用指针能够提高程序运行速度的缘故;委托不与内存打交道,而是把这一工作交给CLR去完成。CLR无法阻止将不安全的代码调用到本机(非托管)代码中或执行恶意操作。然而当代码是类型安全时,CLR的安全性强制机制确保代码不会访问本机代码,除非它有访问本机代码的权限。
    委托派生于基类System.Delegate,不过委托的定义和常规类的定义方法不太一样。委托的定义通过关键字delegate来定义:
    public delegate int myDelegate(int x,int y);
    上面的代码定义了一个新委托,它可以封装任何返回为int,带有两个int类型参数的方法。任何一个方法无论是实例方法还是静态方法,只要他们的签名(参数类型在一个方法中的顺序)和定义的委托是一样的,都可以把他们封装到委托中去。这种签名方法正是保证委托是类型安全的手段之一。
    产生委托实例和产生类实例(对象)差不多,假如我们有如下的方法:
    public int sub(int x,int y)
    {
           return(x+y);
    }
    我们就可以使用如下的代码得到一个委托实例:
    myDelegate calculatin=new myDelegate(sub);
    接下来我们就可以直接使用calculation调用sub方法了:
    calculation(10,3);
    下面我们将用委托重写上面的一个程序来看一下在C#中如何通过委托实现由函数指针实现的功能:
    我们还可以声明一个委托数组,就像声明一个对象数组一样,上面的例子中就使用到了委托数组;一个委托还可以封装多个方法(多路广播委托,经常与事件处理程序结合使用),只要这些方法的签名是正确的。多路广播委托的返回值一般为void,这是因为一个委托只能有一个返回值,如果一个返回值不为void的委托封装了多个方法时,只能得到最后封装的方法的返回值,这可能和用户初衷不一致,同时也会给管理带来不方便。如果你想通过委托返回多个值,最好是使用委托数组,让每个委托封装一个方法,各自返回一个值。
    事件
    在C#中,委托的最基本的一个用处就是用于事件处理。事件是对象发送的消息,以发信号通知操作的发生,通俗一点讲,事件就是程序中产生了一件需要处理的信号。
    事件的定义用关键字event声明,不过声明事件之前必须存在一个多路广播委托:
    public delegate void Calculate(int x,int y);//返回值为void的委托自动成为多路广播委托;
    public event calculate OnCalculate;
    从上节的委托实例和上面的事件的声明可以看出,事件的声明仅仅是比委托实例的声明多了个关键字event,事实上事件可以看作是一个为事件处理过程定制的多路广播委托。因此,定义了事件后,我们就可以通过向事件中操作符+=添加方法实现事件的预定或者是通过-=取消一个事件,这些都与委托实例的处理是相同的。与委托实例不同的是,操作符=对于事件是无效的,即
    OnCalculate=new calculate(sub) ;//无效
    只是因为上面的语句会删除由OnCalculate封装的所有其他方法,指封装了由此语句指定的唯一方法,而且一个预定可以删除其他所有方法,这会导致混乱。
    回调函数
    回调函数是在托管应用程序中可帮助非托管 DLL 函数完成任务的代码。对回调函数的调用将从托管应用程序中,通过一个 DLL 函数,间接地传递给托管实现。在用平台调用调用的多种
    展开全文
  • 委托及组合委托

    2019-08-07 16:51:30
    委托委托对象:通过随机值大小创建相应委托对象。组合委托
  • c# 委托委托

    2013-07-05 16:59:00
    c# 委托c# 委托c# 委托c# 委托
  • 这个里面有自定义委托的实现,多播委托的实现,泛型委托的实现。编译环境为VS2015
  • C#委托 C#委托 C#委托

    2010-06-24 08:30:55
    C#委托 C#委托 C#委托C#委托 C#委托
  • 借款委托书格式_委托借款委托委托贷款是银行的重要中间业务,其具体是指由政府部门、企事业单位及个人等委托人提供资金,由贷款人(即受托人)根据委托人确定的贷款对象、用途、金额期限、利率等代为发放、监督使用并...
  • 主要为大家详细介绍了C#委托与匿名委托的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • kotlin 委托

    2021-01-06 02:50:05
    kotlin 委托 委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。 Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 ...
  • .NET委托 .NET委托

    2013-03-30 14:34:08
    .NET委托:一个C#睡前故事.NET委托
  • 本篇文章是对委托与多播委托进行了详细的分析介绍,需要的朋友参考下
  • csharp委托

    2015-03-19 17:26:24
    my_委托,my_委托,my_委托my_委托
  • 自定义委托

    2018-09-25 16:42:59
    自定义委托类,重写QItemDelegate中的5个函数并用于模型实例.
  • C# 委托 (一)—— 委托、 泛型委托与Lambda表达式

    万次阅读 多人点赞 2018-08-19 20:46:47
    1 委托的含义 2 委托声明、实例化和调用 2.1 委托的声明 2.2 委托的实例化 2.3 委托实例的调用 3 泛型委托 3.1 Func委托 3.2 Action委托 3.3 Predicate委托 4 匿名委托 5 Lambda表达式 5.1 表达式Lambda ...

    目录

    1 委托的含义

    2 委托声明、实例化和调用

    2.1 委托的声明

    2.2 委托的实例化

    2.3 委托实例的调用

    3 泛型委托

    3.1 Func委托

    3.2 Action委托

    3.3 Predicate委托

    4 匿名委托

    5 Lambda表达式

    5.1 表达式Lambda

    5.2 语句Lambda


    1 委托的含义

    当需要将一个方法当作另一个方法的参数时,对于某些语言例如C/C++等,需要用函数指针来处理。而对于C#来说,则使用委托机制。

    例如,当我们需要对一个泛型集合ICollection<T>进行排序时,我们定义一个Sort方法,那么这个方法需要哪些参数才能进行排序呢?首先,肯定需要一个Collection<T>对象作为输入参数,代表要排序的对象集合;然后,Sort方法还需要知道如何比较两个对象,经过比较之后才能决定让哪个对象排在前面。因此,Sort方法需要第二个参数,这个参数是一个方法,代表着比较排序对象的方法。这时候这个比较方法参数就只能是一个委托。我们在调用Sort方法对具体的类进行排序时,通过委托传入一个具体类的比较方法。

    再比如,在使用LINQ时,我们经常会用到Where()、Find()等扩展方法,实现对集合中元素的筛选或查找。Where()方法拥有一个Func<T>参数,它就是一个委托。我们调用Where()时需要给这个委托传递一个方法,告诉Where筛选器筛选元素的规则方法。

    委托是一种特殊的类,是一种能够引用方法的类。在创建委托时,就是创建了一个存储方法引用的对象。

    委托是类型安全的。C函数指针只是一个指向一个存储单元的指针,不能保证指向的内容就是正确类型的函数。而对于C#的委托而言,声明一个委托时必须指定返回类型和参数,.NET编译器会严格检查方法的参数和返回类型和委托是否匹配,检查通过后才能进行转换。转换之后的委托实例作为一个参数,传递给调用它的函数。

    一个委托可以被传递任何符合要求的方法。不同场合需要不同方法时,在调用的地方直接将委托参数替换为实际方法就行。因此,委托调用的方法是在程序运行时才能确定的。

    2 委托声明、实例化和调用

    2.1 委托的声明

    前面提到过,委托是一种特殊的类,因此委托的声明与类的声明方法类似,在任何可以声明类的地方都可以声明委托。委托声明用delegate关键字,同时委托要指明方法参数和返回值,写法与方法类似。综合类的声明和方法的声明,委托声明写成如下形式:

    [访问修饰符] delegate 返回值类型 委托名 (形参列表);

    委托的声明实际上是定义了一个派生于System.Delegate类的类,这与一般类的声明语法不同。编译器会根据委托的声明自动创建一个委托的类并实现细节。

    接下来我们以一个简单的List<Student>排序为例进行说明。假设我们有一个Student类,存放学生信息,拥有姓名、年龄和学号三个属性:

    public class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int Num { get; set; }    
    }

    然后创建一个List<Student>:

    Student s1 = new Student() { Name = "小红", Age = 10, Num = 1001 };
    Student s2 = new Student() { Name = "小华", Age = 9, Num = 1002 };
    List<Student> sList = new List<Student>();
    sList.Add(s1);
    List.Add(s2);

    我们的目标是想给List<Student>对象添加一个排序方法,这个排序方法可以根据年龄或者学号来排序,具体需要哪一种排序需要在客户端调用时指定。(简单起见,本案例中List<Student>只包含两个元素,不纠结于排序算法)

    按照要求,Student对象的比较有两种方法,我们实现两个比较方法,供委托使用:

    //比较年龄
    public static bool Younger(Student s1, Student s2) => s1.Age <= s2.Age;
    
    //比较学号
    public static bool NumSmaller(Student s1, Student s2) => s1.Num <= s2.Num;

    由上,我们可以抽象出一个代表比较Student的方法的委托:

    public delegate bool CompareDelegate(Student first, Student second);

    这个委托的类名为CompareDelegate,注意到委托声明的返回值类型、参数与其代表的方法要完全一致。

    2.2 委托的实例化

    与普通类的使用方法相同,声明了委托之后,我们必须给委托传递一个具体的方法,才能在运行时调用委托实例。委托实例包含了被传递给它的方法的信息,在运行时,调用委托实例就相当于执行它当中的方法。

    委托实例化格式如下:

    委托类名 委托实例名 = new 委托类名(Target) ;

    其中,委托实例名是自定义的名称,Target是要传入的方法的名称。注意,Target是方法的引用,不能带()。带()的话是该方法的调用。区分引用和调用。

    委托的实例化还有一种简单的方法:

    委托类名 委托实例名 = Target;

    在需要委托实例的地方直接传入Target引用即可,C#编译器会自动根据委托类型进行验证,这称为“委托推断”。

    案例:

    //以下两种方法等价
    CompareDelegate myCompareDelegate = new CompareDelegate(Younger);
    CompareDelegate myCompareDelegate = Younger;//委托推断

    2.3 委托实例的调用

    委托实例等价于它当中实际方法,因此可以使用反射的Invoke()方法调用委托实例,也可以直接在委托实例后加上()进行调用。

    我们下面看一下委托所代表的方法是如何被业务方法调用的。这里我们的业务是排序SortStudent方法:

    //使用委托的业务方法
    public static void SortStudent(List<Student> sList, CompareDelegate CompareMethod)
    {
        if (CompareMethod(sList[0], sList[1]))//等价于CompareMethod.Invoke(sList[0],  List[1])
        {
            //sList[0]已经在sList[1]前面了,所以什么也不用做
        }
        else
        {
            sList.Reverse();//交换位置
        }
        //获取排名采用的比较方法的名称
        Console.WriteLine($"\r\n按照 {CompareMethod.Method.Name} 排名:");
        //打印排序后的链表
        foreach (Student s in sList)
            Console.WriteLine($"{s.Name} {s.Age} {s.Num} ");
    }

    这里Sort方法拥有一个CompareDelegate类型的委托实例CompareMethod,它可直接当做具体方法进行调用。

    在客户端对委托进行实例化后,调用SortStudent()方法就可以进行排序了。

    //委托的实例化与使用
    CompareDelegate myCompareDelegate = NumSmaller;//采用比较学号的方法
    SortStudent(sList, myCompareDelegate);
    
    //使用委托推断,与上两行等价
    SortStudent(sList, NumSmaller);

    输出如下:

    按照 NumSmaller 排名:
    小红 10 1001
    小华 9 1002

    3 泛型委托

    我们每次要使用一个委托时,都需要先声明这个委托类,规定参数和返回值类型,然后才能实例化、调用。为了简化这个过程, .NET 框架为我们封装了三个泛型委托类,因此大部分情况下我们不必再声明委托,可以拿来直接实例化使用,方便了我们的日常Coding。

    .这三种泛型委托包括:Func<T>委托、Action<T>委托和Predicate<T>委托。

    3.1 Func<T>委托

    Func<T>委托代表着拥有返回值的泛型委托。Func<T>有一系列的重载,形式如 Func<T1,T2, ... TResult>,其中TResult代表委托的返回值类型,其余均是参数类型。只有一个T时,即Func<TResult>,代表该委托是无参数的。.NET封装了最多16个输入参数的Funct<>委托。

    需要特别注意的是,若方法返回 void ,由于 void 不是数据类型,因此不能定义Func<void>委托。返回 void 的泛型委托见下文的Action<T>。

    Func<T>的使用方法与一般的委托相同。例如上面的案例可改写如下;

    public static void SortStudent(List<Student> sList,Func<Student,Student,bool> CompareMethod)
    {
        if(CompareMethod(sList[0], sList[1]))
        {
        }
        else
        {
            sList.Reverse();
        }
        Console.WriteLine($"\r\n按照 {CompareMethod.Method.Name} 排名:");
        foreach (Student s in sList)
            Console.WriteLine($"{s.Name} {s.Age} {s.Num} ");
    }
    
    //客户端调用
    Func<Student, Student, bool> myCompareFunc = NumSmaller;
    SortStudent2(sList, myCompareFunc);

    注意SortStudent2方法的委托参数也必须是Func<Student, Student, bool>,才能满足方法调用时类型一致的要求。 

    3.2 Action<T>委托

    Action<T>委托代表返回值为空 void 的委托,它也有一些列重载,最多拥有16个输入参数。用法与Func<T>相同。

    3.3 Predicate<T>委托

    这个一般用的较少,它封装返回值为bool类型的委托,可被Func<T>代替。

    4 匿名委托

    采用匿名方法实例化的委托称为匿名委托。

    每次实例化一个委托时,都需要事先定义一个委托所要调用的方法。为了简化这个流程,C# 2.0开始提供匿名方法来实例化委托。这样,我们在实例化委托时就可以 “随用随写” 它的实例方法。

    使用的格式是:

    委托类名 委托实例名 = delegate (args) {方法体代码} ;

    这样就可以直接把方法写在实例化代码中,不必在另一个地方定义方法。当然,匿名委托不适合需要采用多个方法的委托的定义。

    使用匿名方法,以上代码可改写为:

    CompareDelegate anonymousCompare = delegate (Student s3, Student s4) 
    { 
        return s1.Num <= s2.Num; 
    };
    SortStudent(sList, anonymousCompare);

    需要说明的是,匿名方法并不是真的“没有名字”的,而是编译器为我们自动取一个名字。SortStudent方法打印了委托调用的方法的名字(见上文代码),我们可以看到如下输出:

    按照 <Main>b__0 排名:
    小红 10 1001
    小华 9 1002

     编译器为我们的匿名方法取了一个b__0的名字。

    5 Lambda表达式

    江山代有才人出,纵然匿名方法使用很方便,可惜她很快就成了过气网红,没能领多长时间的风骚。如今已经很少见到了,因为delegate关键字限制了她用途的扩展。自从C# 3.0开始,她就被Lambda表达式取代,而且Lambda表达式用起来更简单。Lambda表达式本质上是改进的匿名方法。

    Lambda表达式的灵感可能是来源于数学中的函数表达式,例如下图:

    Lambda表达式把其中的箭头用 => 符号表示。

    如今Lambda表达式已经应用在很多地方了,例如方法体表达式(Expression-Bodied Methods)、自动只读属性表达式等等。

    Lambda表达式形式上分为两种:

    5.1 表达式Lambda

    当匿名函数只有一行代码时,可采用这种形式。例如:

    CompareDelegate LambdaCompare = (s4, s5) => s4.Age <= s5.Age;

    其中=>符号代表Lambda表达式,它的左侧是参数,右侧是要返回或执行的语句。参数要放在圆括号中,若只有一个参数,为了方便起见可省略圆括号。有多个参数或者没有参数时,不可省略圆括号。

    相比匿名函数,在表达式Lambda中,方法体的花括号{}和return关键字被省略掉了。

    其实,上文定义NumSmaller()和Younger()方法时,由于这两个方法主体只有一行代码,所以用的也是表达式Lambda,这是Lambda表达式的推广, 是C# 6 编译器提供的一个语法糖。

    5.2 语句Lambda

    当匿名函数有多行代码时,只能采用语句Lambda。例如,上面的表达式Lambda可改写为语句Lambda:

    CompareDelegate LambdaCompare = (s4, s5) => 
    {
        return s4.Age <= s5.Age;
    };

    语句Lambda不可以省略{}和return语句。

    展开全文
  • 委托异步调用

    2018-08-27 10:15:37
    委托异步调用委托
  • 委托租赁合同及授权委托书.doc
  • 委托代理理论中知情委托人模型,李建标,宗计川,标准的委托代理理论中,一般只注重代理人信息优势和委托人信息劣势的情形。而这只是问题的一个方面,有时候委托人和代理人的信息
  • 委托书之危险品委托运输合同.zip
  • 全权委托授权委托书(范本).doc
  • 委托书之危险品委托运输合同.txt
  • 委托书之危险品委托运输合同.pdf
  • 委托合同(供委托培训用).doc
  • 收款委托书(企业委托企业).doc
  • 委托的作用和好处

    千次阅读 2020-07-19 19:35:02
    委托

    什么是委托?

        从程序的角度来讲:你就可以把委托看成是用来执行方法(函数)的一个“指针” 通俗的说就是:通过委托,我们可以把方法当成参数传递。

    示例

        这里我举个例子:“设想,如果我们写了一个厨师做菜的方法,里面有拿菜、切菜、配菜、炒菜四个步骤,但编写此方法代码的人想让配菜这个环节让调用方法的人来实现,换句话说,就是想把方法作为参数来传递,那么怎么来实现呢?
        拿菜、切菜、配菜、炒菜我们定义4个方法,抽象成一个做菜类(ZuoCai.cs)。

     public class ZuoCai
     {	 
    	 // 拿菜方法
    	 public void NaCai()
    	 {
    		 Console.WriteLine("A负责拿菜!");
    	 }
    	 
    	 // 切菜方法
    	 public void QieCai()
    	 {
    		 Console.WriteLine("B负责切菜!");
    	 }
    	 
    	 // 配菜方法
    	 public void PeiCai()
    	 {
    		 Console.WriteLine("C负责配菜!");
    	 }
    	 
    	 // 炒菜方法
    	 public void ChaoCai()
    	 {
    		 Console.WriteLine($"D负责炒菜!");
    	 }
    	 
    	 // 开始做菜的方法
    	 // <param name="zuoCaiDelegate">委托作为参数传递</param>
    	 public void StartZuoCai(ZuoCaiDelegate zuoCaiDelegate)
    	 {
    		 zuoCaiDelegate();
    	 }
     }
    

        委托通过关键词delegate来定义,如下:

    //定义一个做菜的委托
    public delegate void ZuoCaiDelegate(int number);
    

        当然在c#4.0后,微软为我们简化了委托的定义,帮我们定义了Action和Func两种委托类型,但是原理都是通过delegate来实现,这里不跑题,有兴趣的关注我的其他章节。
        定义了委托后,如何使用委托呢,委托要使用,必须实例化,如下:

    //实例化一个做菜类
     ZuoCai zuoCai = new ZuoCai();
    //实例化一个做菜的委托
    ZuoCaiDelegate zuoCaiDelegate = new ZuoCaiDelegate(zuoCai.NaCai);
    //把多个方法绑定到委托上。
    zuoCaiDelegate += zuoCai.QieCai;
    zuoCaiDelegate += zuoCai.PeiCai;
    zuoCaiDelegate += zuoCai.ChaoCai;
    

        委托可以绑定多个方法,支持+=(添加方法)和-=(取消方法),上面的定义就是将做菜的4个方法按顺序绑定在委托上,执行时候也是按顺序执行。

        今天开店后,接了一个客户,然后店长安排做菜,如下:

     public void Main()
     {
    	 //实例化一个做菜类
    	 ZuoCai zuoCai = new ZuoCai();
    	 //实例化一个做菜的委托
    	 ZuoCaiDelegate zuoCaiDelegate = new ZuoCaiDelegate(zuoCai.NaCai);
    	 zuoCaiDelegate += zuoCai.QieCai;
    	 zuoCaiDelegate += zuoCai.PeiCai;
    	 zuoCaiDelegate += zuoCai.ChaoCai;
    	 //开始做菜
    	 zuoCai.StartZuoCai(zuoCaiDelegate);
     }
    

    在这里插入图片描述
        忽然来了一个湖南的湘妹子客户,要求配菜时候多放点辣椒,怎么办呢?
        方法1:店长要求改写ZuoCai类中的PeiCai方法。
        如果又来了一个广东的客户,要求放点糖,难道又要改方法吗?如果很多各种各样需求的用户,都要靠改动原有的方法来实现,这个显然不合理的,违法了设计模式中的开闭原则。
        方法2:店长要求在调用地方重新添加一个配菜方法(对扩展开放),这个是合理的建议,如下:

     // 配菜方法,加辣
     void PeiCaiJiaLa()
     {
    	 Console.WriteLine("C负责配菜,要求多方点辣椒!");
     }
    

        只需要把调用方的
        zuoCaiDelegate += zuoCai.PeiCai;
        改为
        zuoCaiDelegate += PeiCaiJiaLa;
        完整代码如下:

     //实例化一个做菜类
     ZuoCai zuoCai = new ZuoCai();
     //实例化一个做菜的委托
     ZuoCaiDelegate zuoCaiDelegate = new ZuoCaiDelegate(zuoCai.NaCai);
     zuoCaiDelegate += zuoCai.QieCai;
     zuoCaiDelegate += PeiCaiJiaLa; //改为新定义的方法
     zuoCaiDelegate += zuoCai.ChaoCai;
     //开始做菜
     zuoCai.StartZuoCai(zuoCaiDelegate);
    

    在这里插入图片描述

    展开全文
  • 委托上户口委托书怎么写 为办理户口写一份委托书,*是小编为大家整理的委托上户口委托书,仅供参考。委托上户口委托书篇一: 被代理人 因故无法自己前来办理户口迁移,特委托 代理户口迁移手续,将被代理人的户口自...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 329,071
精华内容 131,628
关键字:

委托