C#沉淀之委托的深入讲解

什么是委托

要传递方法,就必须把方法的细节封装在一钟新类型的对象中,即委托。委托是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托只包含一个或多个方法的地址。

.NET版本中,委托指向方法的地址。在C++中,函数指针是一个指向内存位置的指针,但它不是类型安全的。开发者无法判断这个指针实际指向什么,像参数和返回值等项就更不知道了。

.NET委托是类型安全的类,它定义了返回类型和参数的类型。委托类不仅包含对方法的引用,也可以包含对多个方法的引用。

可以认为委托是持有一个或多个方法的对象。委托可以被执行,执行委托时委托会执行它所“持有”的方法

代码示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 //使用关键字delegate声明委托类型
 //委托是一种类型,所以它与类属于同一级别
 //注意:这里是委托类型,而不是委托对象
 delegate void MyDel(int value);

 class Program
 {

  void PrintLow(int value)
  {
   Console.WriteLine("{0} - Low Value", value);
  }

  void PrintHigh(int value)
  {
   Console.WriteLine("{0} - High Value", value);
  }

  static void Main(string[] args)
  {
   //实例化Program类,以访问PrintLow和PrintHigh方法
   Program program = new Program();

   //声明一个MyDel类型的委托
   MyDel del;

   //创建随机数
   Random rand = new Random();
   int randomvalue = rand.Next(99);

   //使用三目运算符根据当前随机数的值来创建委托对象
   del = randomvalue < 50
    ? new MyDel(program.PrintLow)
    : new MyDel(program.PrintHigh);

   //执行委托
   del(randomvalue);

   Console.ReadKey();
  }
 }
}

从上例可以看出,使用委托的首先得通过关键字delegate声明一个委托类型,这个委托类型包括返回值、名称、签名;当类型声明好以后,需要通过new来创建委托对象,创建对象时的参数是一个方法,这个方法的签名和返回类型必须与该委托类型定义的签名一致;调用委托时,直接通过实例化的委托对象名,并提供参数即可,然后委托会执行在其所持有的方法

委托与类

委托和类一样,是一种用户自定义的类型;不同的是类表示的是数据和方法的集合,而委托持有一个或多个方法,以及一系列预定义操作

委托的使用步骤

  • 声明一个委托类型
  • 使用该委托类型声明一个委托变量
  • 创建委托类型的对象,把它赋值给委托变量;委托对象中包括指向某个方法的引用,此方法和委托类型定义的签名与返回类型需要一致
  • 增加更多的方法(可选)
  • 像调用方法一样调用委托(委托中的包含的每一个方法都会被执行)

delegate的原则

delegate相当于一个包含有序方法列表的对象,这些方法都具有相同的签名和返回类型

方法的列表称为调用列表

委托保存的方法可以来自任何类或结构,只要它们在以下两点匹配:

  • 委托的返回类型
  • 委托的签名(包括ref和out修饰符)

调用列表中的方法可以是静态方法也可以是实例方法

在调用委托的时候,会调用列表中的所有方法

声明委托类型

如下,delegate关键字开关,然后是返回类型,再定义名称与签名

delegate void MyDel(int vallue);

返回类型与签名指定了委托接受的方法形式

注意:委托类型是没有方法主体的

创建委托对象

使用new运算符创建对象

MyDel del = new MyDel(object.Func); //object.Func是个实例方法
Mydel _del = new MyDel(Object.Func); //Object.Func是个静态方法

使用快捷语法创建对象

MyDel del = object.Func; //object.Func是个实例方法
Mydel _del = Object.Func; //Object.Func是个静态方法

这种语法是能够工作是因为在方法名称和其相应的委托类型之间存在隐式的转换

创建委托对象后会将指定的方法加入到委托的调用列表中

由于委托是引用类型,可以通过赋值来改变包含在委托变量中的引用,如下:

MyDel del;
del = new MyDel(object.FuncA); //创建第一个对象
del = new MyDel(object.FuncB); //创建第二个对象

由于第二个对象也赋值给了变量del,因此del所引用的第一个对象将被垃圾回收器回收

组合委托

//创建两个委托
MyDel del_A = new MyDel(object.FuncA);
Mydel del_B = new MyDel(object.FuncA);

//组合委托
MyDel del_C = del_A + del_B;

当将del_A与del_B通过+进行组合后,会返回一个新的委托对象,该对象将del_A与del_B中的方法调用列表组合到新的对象里,该新对象赋值给变量del_C,所以执行del_C的时候,会执行del_A与del_B中所保存的方法object.FuncA和object.FuncA

委托添加多个方法

MyDel del = object.FuncA; //创建并初始化委托对象
del += object.FuncB; //增加方法
del += object.FuncC; //增加方法

通过+=符号为委托对象添加更多方法,上例中,del对象不保存了三个方法,在执行del时,这三个方法会被依次调用

注意,在使用+=为委托对象添加新的方法时,实际上是创建了一个新的委托对象(原对象的副本)

移除委托方法

del -= object.FuncB; //移除方法
del -= object.FuncC; //移除方法

通过-=来将委托调用列表中已保存的方法,移除动作是从调用列表的最后一个方法开始匹配,一次只会移除一条匹配的方法,如果调用列表中不存在该方法,则没有任何效果;如果试图调用一个空的委托则会发生异常

注意,在使用-=为委托对象移除方法时,实际上是创建一个新的委托对象(原对象的副本)

调用委托

调用委托就像调用方法一样

示例:MyDel类型参考上面的定义

MyDel del = object.FuncA; //创建并初始化委托对象
del += object.FuncB; //增加方法
del += object.FuncC; //增加方法

//调用委托
del(55);

参数55会在调用委托对象时依次传递给保存的方法

一个完整的委托示例代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 class Program
 {
  //定义委托类型
  delegate void PrintFunction(string txt);

  //测试类中定义三个方法
  class Test
  {
   public void PrintA(string txt)
   {
    Console.WriteLine("printA:{0}", txt);
   }

   public void PrintB(string txt)
   {
    Console.WriteLine("printB:{0}", txt);
   }

   public static void PrintC(string txt)
   {
    Console.WriteLine("printC:{0}", txt);
   }
  }
  static void Main(string[] args)
  {
   Test test = new Test();
   PrintFunction pf;

   //实例化并创建委托对象
   pf = test.PrintA;

   //为委托对象增加方法
   pf += test.PrintB;
   pf += Test.PrintC;
   pf += test.PrintA; //添加一个重复的方法

   //通过与null比较,确认委托对象中保存了方法
   if (pf != null)
    pf("Hello");
   else
    Console.WriteLine("pf是个空委托!");

   Console.ReadKey();
  }
 }
}

调用带有返回值的委托

如何委托有返回值,并且调用列表中有一个以上的方法,那么将使用最后一个方法的返回值,之前方法的返回值被忽略

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 class Program
 {
  //定义委托类型
  delegate int DelFunction();

  //测试类中定义三个方法
  class Test
  {
   int IntValue = 0;

   public int FuncA()
   {
    return IntValue += 1;
   }

   public int FuncB()
   {
    return IntValue += 10;
   }
  }
  static void Main(string[] args)
  {
   Test test = new Test();
   DelFunction df;

   df = test.FuncA;
   df += test.FuncB;

   //最终返回值的是11
   if (df != null)
    Console.WriteLine("返回值:"+df());
   else
    Console.WriteLine("pf是个空委托!");

   Console.ReadKey();
  }
 }
}

具有引用参数的委托

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 //定义委托类型
 delegate void MyDel(ref int x);
 class Program
 {

  static void Add1(ref int x) { x += 1; }
  static void Add2(ref int x) { x += 2; }

  static void Main(string[] args)
  {
   Program program = new Program();

   MyDel del = Add1;
   del += Add2;

   //ref会将x当作引用值传递给委托方法
   int x = 5;
   del(ref x);

   Console.ReadKey();
  }
 }
}

在调用Add1方法时,x = 5+1,再调用Add2方法时,不是x = 5+2而是x = 6 +2

参考:ref按引用传递参数

在方法的参数列表中使用 ref 关键字时,它指示参数按引用传递,而非按值传递。 按引用传递的效果是,对所调用方法中参数进行的任何更改都反映在调用方法中。 例如,如果调用方传递本地变量表达式或数组元素访问表达式,所调用方法会替换 ref 参数引用的对象,然后,当该方法返回时,调用方的本地变量或数组元素将开始引用新对象

匿名方法

匿名方法是在初始化委托时内联声明的方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 //定义委托类型
 delegate void MyDel(ref int x);
 class Program
 {

  static void Add1(ref int x) { x += 1; }
  static void Add2(ref int x) { x += 2; }

  static void Main(string[] args)
  {
   Program program = new Program();

   //采用匿名方法形式代替具名方法
   MyDel del = delegate(ref int y) { y += 3; };
   del += Add1;
   del += Add2;

   //ref会将x当作引用值传递给委托方法
   int x = 5;
   del(ref x);

   Console.ReadKey();
  }
 }
}

在声明委托变量时作为初始化表达式,或在为委托增加事件时使用

语法解析

以关键字delegate开头;后跟小括号提供参数;再后跟{}作为语句块

delegate (Parameters) {ImplementationCode}

  • 匿名方法不会显示的声明返回类型delegate (int x) { return x;}即为返回一个int类型的值
  • 参数的数量、位置、类型、修饰符必须与委托相匹配
  • 可以通过省略圆括号或使圆括号为空来简化匿名方法的参数列表,前提是参数是不包含out参数,方法体中不使用任何参数

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 //定义委托类型
 delegate void MyDel(ref int x);
 class Program
 {

  static void Add1(ref int x) { x += 1; }
  static void Add2(ref int x) { x += 2; }

  static void Main(string[] args)
  {
   Program program = new Program();

   //采用匿名方法形式代替具名方法
   MyDel del = delegate(ref int y) { y += 3; };
   del += Add1;
   del += Add2;

   //匿名方法未使用任何参数,简化形式
   del += delegate{int z = 10;};

   //ref会将x当作引用值传递给委托方法
   int x = 5;
   del(ref x);

   Console.ReadKey();
  }
 }
}

如果定义一个带有params形式的参数,在使用匿名方法的时候可以省略params关键字以简化代码

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForDelegate
{
 //定义一个带有params形式参数的委托类型
 delegate void DelFunction(int x, params int[] z);
 class Program
 {
  static void Main(string[] args)
  {
   Program program = new Program();

   // 关键字params被忽略(省略关键字以简化)
   DelFunction df = delegate(int x, int[] y) { ... };

   Console.ReadKey();
  }
 }
}

Lambda表达式

Lambda可以简化匿名方法,语法形式如下:

(参数) => {语句块} // => 读作 gose to

参数中的类型可以省略
如果只有一个参数,圆括号可以省略
如果没有参数,圆括号不可以省略
语句块如果只有一行代码,花括号可以省略

示例:

MyDel del = delegate(int y) { return y += 3; }; //匿名方法
MyDel del1 = (int y) => {return y += 3;} // Lambda表达式
MyDel del2 = (y) => {return y += 3;} // 省略参数类型
MyDel del3 = y => y += 3; // 省略圆括号和花括号,虽然没有return,但仍会返回y的值

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • C#匿名委托与Lambda表达式详解

    通过使用匿名委托(匿名方法),使编程变得更加灵活,有关委托与匿名委托请参考我的前一篇Blog<委托与匿名委托>. 继续之前示例,代码如下: static void Main(string[] args) { Worker.TwoNumberHandleMethodDelegate method = delegate(int a, int b) { return a + b; }; Worker worker = new Worker(); int result = worker.HandleT

  • C#使用委托的形式调用线程代码实例

    委托 对于委托,我们都知道他是一个引用类型,具有引用类型所具有的通性.需要知道的是它保存的不是实际值,只是是保存对存储在托管堆中的对象的引用.或说的直接点,委托就相当于叫人帮忙,让你帮你做一些事情.我这里就使用委托的形式,调用线程,来简单的说一下. 代码如下: using System; using System.Threading; namespace _012_线程 { class Program { static void Main(string[] args) //在mian中线程是执行

  • C#基于委托实现多线程之间操作的方法

    本文实例讲述了C#基于委托实现多线程之间操作的方法.分享给大家供大家参考,具体如下: 有的时候我们要起多个线程,更多的时候可能会有某个线程会去操作其他线程里的属性. 但是线程是并发的,一般的调用是无法实现我们的要求的. 于是,我们在这里就可以用委托,代码如下 private delegate void DelegateInfo(); private delegate void DelegateIsEnd(); //这个是线程调用其他线程的方法 private void Dowork() { //

  • C#委托与匿名委托详解

    本来是想写一篇<委托与lambda表达式的前世今生>,但仅委托部分已经写了很多内容,于是就此分开关于Lambda表达是的内容后续再写吧. 不知道Lambda表达式是谁发明的,只记得第一次接触Lambda表达式是在使用VS2008的时候,那就先认为是微软发明的吧. Lambda表达式从我接触开始到现在变得越来越流行,Java8中开始支持.kotlin更是对C#,F#做了广泛的抄袭(C#曾几何时不也如此对待过Java嘛).其实这都充分说明了,Lambda表达式的重要性.要搞清楚Lambda首先需要

  • 详解C#中委托,事件与回调函数讲解

    .Net编程中最经常用的元素,事件必然是其中之一.无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等. "protected void Page_Load(object sender, EventArgs e)"这段代码相信没有人不熟悉的.细心一点一定会发现,非常多的事件方法都是带了"object sender, EventArgs e"这两个参数.这是不是和委托非常相似呢? 一.委托(有些书中也称为委派)

  • 详解C#中通过委托来实现回调函数功能的方法

    委托(delegate)是一种可以把引用存储为函数的类型,这类似于c++中的函数指针. 回调函数 c++中的回调函数,就是用函数指针来实现的.类似的,c#中用委托,来实现回调函数的功能. 回调函数为什么被称为回调函数?比如你调用了一个函数,那么就叫调用,但是如果你在调用一个函数的时候,还需要把一个函数提供给该函数,让这个函数来调用你的函数,那么你提供的这个函数就被称为回调函数(callback). 对于python这样的动态语言而言,就没有c#,c++提供特殊的语法实现回调函数,因为在pytho

  • c#中的泛型委托详解

    今天学习一下c#中的泛型委托. 1.一般的委托,delegate,可以又传入参数(<=32),声明的方法为  public delegate void SomethingDelegate(int a); using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace delegateSummary { publ

  • C#中的委托数据类型简介

    什么是委托? 委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法).通俗的说,委托是一个可以引用方法的对象,当创建一个委托,也就创建一个引用方法的对象,进而就可以调用那个方法,即委托可以调用它所指的方法. 来看下面的例子,类deleMthod定义了3个方法,add.minus和multi,他们都具有相同的输入参数列表(int x,int y)和输出参数类型int,那么我们就说这三个方法具有相同的方法签名.开发者可以抽象地用 int 某名称(int x,int y) 的一种

  • c# 委托详解

    委托是一个类型.C#中的委托是面向对象的,并且它是类型安全的 当创建委托实例的时候,创建的实例会包含一个调用列表,在调用列表中可以包含多个方法.每个方法称作一个调用实体.调用实体可以是静态方法,也可以是实例方法.如果是实例方法,则该调用实体包含调用该实例方法的实例.委托并不关心它所调用方法所属的类,它只关心被调用方法与委托的类型是否兼容. 下面是代码实例: using System; namespace LycheeTest{ public delegate void D(int a, int

  • 详解C#中的委托

    委托这个东西不是很好理解,可是工作中又经常用到,你随处可以看到它的身影,真让人有一种又爱又恨的感觉,我相信许多人被它所困扰过. 一提到委托,如果你学过C语言,你一定会马上联想到函数指针. 什么是委托?委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.委托可以把函数做为参数传递,其实际意义便是让别人代理你的事情.委托可以看做是函数的指针,整数可以用整数变量指向它,对象可以用对象变量指向它, 函数也可以用委托变量指向它.我们可以选择将委托类型看做只定义了一个方法的接口,而委托的实

  • C#中委托(Delegates)的使用方法详解

    1. 委托是什么? 其实,我一直思考如何讲解委托,才能把委托说得更透彻.说实话,每个人都委托都有不同的见解,因为看问题的角度不同.个人认为,可以从以下2点来理解: (1) 从数据结构来讲,委托是和类一样是一种用户自定义类型. (2) 从设计模式来讲,委托(类)提供了方法(对象)的抽象. 既然委托是一种类型,那么它存储的是什么数据? 我们知道,委托是方法的抽象,它存储的就是一系列具有相同签名和返回回类型的方法的地址.调用委托的时候,委托包含的所有方法将被执行. 2. 委托类型的定义 委托是类型,就

  • C# WPF 通过委托实现多窗口间的传值的方法

    在使用WPF开发的时候就不免会遇到需要两个窗口间进行传值操作,当然多窗口间传值的方法有很多种,本文介绍的是使用委托实现多窗口间的传值. 在上代码之前呢,先简单介绍一下什么是C#中的委托(如果只想了解如何传值可以略过这部分)在网络上有很多对于委托的介绍和讲解,经过我的学习和总结加上了一点我自己的理解,我认为委托是一种类似于C语言的指针,但是它指向的是方法而不是变量.如果把委托看作一个变量,那么这个变量里存着的就是你目标方法的地址,调用委托约等于调用你的目标方法.(个人理解欢迎指正交流) 以下正文:

  • 浅谈C#中的委托、事件与异步

    从刚接触c#编程到现在,差不多快有一年的时间了.在学习过程中,有很多地方始终似是而非,直到最近才弄明白. 本文将先介绍用法,后评断功能. 一.委托 基本用法: 1.声明一个委托类型.委托就像是'类'一样,声明了一种委托之后就可以创建多个具有此种特征的委托.(特征,指的是返回值.参数类型) public delegate void SomeKindOfDelegate(string result); 2.创建一个在1中创建的委托类型的委托. public SomeKindOfDelegate aD

随机推荐