C#中的delegate委托类型基本学习教程

委托
delegate 是表示对具有特定参数列表和返回类型的方法的引用的类型。在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。你可以通过委托实例调用方法。
委托用于将方法作为参数传递给其他方法。事件处理程序就是通过委托调用的方法。你可以创建一个自定义方法,当发生特定事件时,某个类(如 Windows 控件)就可以调用你的方法。下面的示例演示了一个委托声明:

public delegate int PerformCalculation(int x, int y);

可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托。该方法可以是静态方法,也可以是实例方法。这样便能通过编程方式来更改方法调用,还可以向现有类中插入新代码。
注意:在方法重载的上下文中,方法的签名不包括返回值。但在委托的上下文中,签名包括返回值。换句话说,方法和委托必须具有相同的返回类型。
将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。例如,对比较两个对象的方法的引用可以作为参数传递到排序算法中。由于比较代码在一个单独的过程中,因此可通过更常见的方式编写排序算法。
委托概述
委托具有以下属性:

  1. 委托类似于 C++ 函数指针,但它们是类型安全的。
  2. 委托允许将方法作为参数进行传递。
  3. 委托可用于定义回调方法。
  4. 委托可以链接在一起;例如,可以对一个事件调用多个方法。
  5. 方法不必与委托类型完全匹配。
  6. C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递来代替单独定义的方法。C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。这些功能现在统称为匿名函数。

使用委托

委托是安全封装方法的类型,类似于 C 和 C++ 中的函数指针。与 C 函数指针不同的是,委托是面向对象的、类型安全的和可靠的。委托的类型由委托的名称确定。以下示例声明名为 Del 的委托,该委托可以封装采用字符串作为参数并返回 void 的方法:

public delegate void Del(string message);

委托对象通常通过提供委托将封装的方法的名称或使用匿名方法构造。对委托进行实例化后,委托会将对其进行的方法调用传递到该方法。调用方传递到委托的参数将传递到该方法,并且委托会将方法的返回值(如果有)返回到调用方。这被称为调用委托。实例化的委托可以按封装的方法本身进行调用。例如:

 // Create a method for a delegate.
public static void DelegateMethod(string message)
{
  System.Console.WriteLine(message);
}

 // Instantiate the delegate.
Del handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

委托类型派生自 .NET Framework 中的 Delegate 类。委托类型是封装的,它们不能派生出其他类,也不能从 Delegate 派生出自定义类。由于实例化的委托是一个对象,因此可以作为参数传递或分配给一个属性。这允许方法作为参数接受委托并在稍后调用委托。这被称为异步回调,是在长进程完成时通知调用方的常用方法。当以这种方式使用委托时,使用委托的代码不需要知道要使用的实现方法。功能类似于封装接口提供的功能。
回调的另一个常见用途是定义自定义比较方法并将该委托传递到短方法。它允许调用方的代码成为排序算法的一部分。以下示例方法使用 Del 类型作为参数:

public void MethodWithCallback(int param1, int param2, Del callback)
{
  callback("The number is: " + (param1 + param2).ToString());
}

然后,你可以将上面创建的委托传递到该方法:

MethodWithCallback(1, 2, handler);

并将以下输出接收到控制台:

The number is: 3

以抽象方式使用委托时,MethodWithCallback 不需要直接调用控制台,记住,其不必设计为具有控制台。 MethodWithCallback 的作用是简单准备字符串并将字符串传递到其他方法。由于委托的方法可以使用任意数量的参数,此功能特别强大。
当委托构造为封装实例方法时,委托将同时引用实例和方法。委托不知道除其所封装方法以外的实例类型,因此委托可以引用任何类型的对象,只要该对象上有与委托签名匹配的方法。当委托构造为封装静态方法时,委托仅引用方法。请考虑以下声明:

public class MethodClass
{
  public void Method1(string message) { }
  public void Method2(string message) { }
}

加上之前显示的静态 DelegateMethod,我们现在已有三个 Del 实例可以封装的方法。
调用时,委托可以调用多个方法。这被称为多播。若要向委托的方法列表(调用列表)添加其他方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。例如:

MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;

//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

此时,allMethodsDelegate 的调用列表中包含三个方法,分别为 Method1、Method2 和 DelegateMethod。原有的三个委托(d1、d2 和 d3)保持不变。调用 allMethodsDelegate 时,将按顺序调用所有三个方法。如果委托使用引用参数,引用将按相反的顺序传递到所有这三个方法,并且一种方法进行的任何更改都将在另一种方法上见到。当方法引发未在方法内捕获到的异常时,该异常将传递到委托的调用方,并且不会调用调用列表中的后续方法。如果委托具有返回值和/或输出参数,它将返回上次调用方法的返回值和参数。若要删除调用列表中的方法,请使用减法运算符或减法赋值运算符(“+”或“+=”)。例如:

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;

由于委托类型派生自 System.Delegate,因此可以在委托上调用该类定义的方法和属性。例如,若要查询委托调用列表中方法的数量,你可以编写:

int invocationCount = d1.GetInvocationList().GetLength(0);

调用列表中具有多个方法的委托派生自 MulticastDelegate,该类属于 System.Delegate 的子类。由于这两个类都支持 GetInvocationList,因此在其他情况下,上述代码也将产生作用。
多播委托广泛用于事件处理中。事件源对象将事件通知发送到已注册接收该事件的接收方对象。若要注册一个事件,接收方需要创建用于处理该事件的方法,然后为该方法创建委托并将委托传递到事件源。事件发生时,源调用委托。然后,委托将对接收方调用事件处理方法,从而提供事件数据。给定事件的委托类型由事件源确定。有关详细信息,请参阅事件(C# 编程指南)。
在编译时比较分配的两个不同类型的委托将导致编译错误。如果委托实例是静态的 System.Delegate 类型,则允许比较,但在运行时将返回 false。例如:

delegate void Delegate1();
delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
  // Compile-time error.
  //Console.WriteLine(d == e);

  // OK at compile-time. False if the run-time type of f
  // is not the same as that of d.
  System.Console.WriteLine(d == f);
}

带有命名方法的委托与带有匿名方法的委托

委托可以与命名方法关联。使用命名方法对委托进行实例化时,该方法将作为参数传递,例如:

// Declare a delegate:
delegate void Del(int x);

// Define a named method:
void DoWork(int k) { /* ... */ }

// Instantiate the delegate using the method as a parameter:
Del d = obj.DoWork;

这被称为使用命名的方法。使用命名方法构造的委托可以封装静态方法或实例方法。在早期版本的 C# 中,命名方法是对委托进行实例化的唯一方式。但是,在不希望付出创建新方法的系统开销时,C# 使您可以对委托进行实例化,并立即指定委托在被调用时将处理的代码块。代码块可以包含 lambda 表达式或匿名方法。

备注:作为委托参数传递的方法必须与委托声明具有相同的签名。
委托实例可以封装静态或实例方法。
尽管委托可以使用 out 参数,但建议您不要将其用于多路广播事件委托,因为您无法知道哪个委托将被调用。
示例 1
以下是声明及使用委托的一个简单示例。注意,委托 Del 和关联的方法 MultiplyNumbers 具有相同的签名

// Declare a delegate
delegate void Del(int i, double j);

class MathClass
{
  static void Main()
  {
    MathClass m = new MathClass();

    // Delegate instantiation using "MultiplyNumbers"
    Del d = m.MultiplyNumbers;

    // Invoke the delegate object.
    System.Console.WriteLine("Invoking the delegate using 'MultiplyNumbers':");
    for (int i = 1; i <= 5; i++)
    {
      d(i, 2);
    }

    // Keep the console window open in debug mode.
    System.Console.WriteLine("Press any key to exit.");
    System.Console.ReadKey();
  }

  // Declare the associated method.
  void MultiplyNumbers(int m, double n)
  {
    System.Console.Write(m * n + " ");
  }
}

输出:

  Invoking the delegate using 'MultiplyNumbers':
  2 4 6 8 10

示例 2
在下面的示例中,一个委托被同时映射到静态方法和实例方法,并分别返回特定的信息。

// Declare a delegate
delegate void Del();

class SampleClass
{
  public void InstanceMethod()
  {
    System.Console.WriteLine("A message from the instance method.");
  }

  static public void StaticMethod()
  {
    System.Console.WriteLine("A message from the static method.");
  }
}

class TestSampleClass
{
  static void Main()
  {
    SampleClass sc = new SampleClass();

    // Map the delegate to the instance method:
    Del d = sc.InstanceMethod;
    d();

    // Map to the static method:
    d = SampleClass.StaticMethod;
    d();
  }
}

输出:

  A message from the instance method.
  A message from the static method.
时间: 2016-01-28

C# 委托(delegate) 的小例子

代码如下: 复制代码 代码如下: static void Main(string[] args)        {           Console.WriteLine(Exec(GetSet));           Console.ReadKey();        }        //定义委托,用于将方法做为参数传给Exec.        public delegate string GetResultDelegate();        public static string G

深入理解C#中的Delegate

在c#中,event与delegate是两个非常重要的概念.因为在Windows应用程序中,对事件的使用非常频繁,而事件的实现依赖于delegate. 下面是对网上一些比较好的关于delegage的资料的整理,以及自己的一些想法. Delegate是什么? Delegate中文翻译为"委托".Msdn中对Delegate的解释如下: C#中的委托类似于C或C++中的函数指针.使用委托使程序员可以将方法引用封装在委托对象内.然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时

C# interface与delegate效能比较的深入解析

前言以前在Code Complete 2nd(代码大全2)这本书上看过说在像是C#这种类型语言中能不要用delegate就尽量不要用,多使用interface取代,以避免效能上的影响实践出真理,所以我就写了个小范例来测试我的硬件是2.66G 4核心CPU,内存4G 我不知道是不是电脑比较快,以及我写的函数太小的关系次数到了10000000次才看到有影响 到了100000000次后看起来也是还好总而分析,还是会有影响需要高效运算或是在嵌入式中,应该还是要多注意一点代码 复制代码 代码如下: usi

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

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

C#匿名方法与Delegate类型转换错误分析

本文实例分析了C#匿名方法与Delegate类型转换错误.分享给大家供大家参考.具体分析如下: 问题描述 C#2.0出现了匿名方法, 这在一定程度上节省了我们维护代码上下文的精力, 也不需要思考为某个方法取什么名字比较合适. 在FCL的一些方法中要求传入一个Delegate类型的参数, 比如Control.Invoke或者Control.BeginInvoke方法: 复制代码 代码如下: public object Invoke(Delegate method); public IAsyncRe

C#使用委托(delegate)实现在两个form之间传递数据的方法

本文实例讲述了C#使用委托(delegate)实现在两个form之间传递数据的方法.分享给大家供大家参考.具体分析如下: 关于Delegate[代理.委托]是C#中一个非常重要的概念,向前可以推演到C++的指针,向后可以延续到匿名方法.lambda表达式. 现在我就从一个最简单最实用的一个小例子出发分析一下Delegate的使用. 现在有两个窗体Form1和Form2. 两个按钮Button1(Form)和Button2(Form2). Form1的代码: private void button

C#委托delegate实例解析

所谓c#的委托就是说把函数当参数来传递. 这个在js完全就用不着搞什么委托东西,直接转就是了.而对于C#来说则不是这样! 一个函数,如果它的参数是函数,那么是这样子写的 : public void method(Action<string, Int32> voidMethod, Func<string, Int32> returnMethod) Action<string, Int32> voidMethod 意思是说这个将被传进来的函数是一个没有return的函数,就

C#事件处理和委托event delegate实例简述

本文实例讲述了C#事件处理和委托event delegate,分享给大家供大家参考.具体方法如下: 以下仅仅是用最简单的方式表示事件,实际应用可能是不同窗体之间相互通知某些操作,达到触发. 首先声明一个degate的 EventHandler 参数可以没有 一个或多个 但是触发和使用一定要匹配. 创建一个该EvenHandler的实例a 在程序建立或你需要的时候产生一个事件触发申明: a += new EventHandler(d); public delegate void EventHand

java实现事件委托模式的实例详解

java实现事件委托模式的实例详解 举例说明: 一个班级,有两类学生,A类:不学习,玩,但是玩的东西不一样,有的是做游戏,与的是看电视(有点不合理) B类:放哨的学生,专门看老师的动向,如果老师进班了就立即通知大家. 如此就形成了一个需求,放哨的学生要通知所有玩的学生:老师来了,而不同的学生有不同的反应,有的马上把电视关闭,有的停止玩游戏. 设计的要求如下,让A类学生和B类学生完全解耦,即A类完全不知道B类的学生,却可以通知B类的学生. 代码及说明如下: Event 类,定义了一个事件类: pa

python GUI库图形界面开发之PyQt5信号与槽事件处理机制详细介绍与实例解析

PyQt5中信号与槽可以说是对事件处理机制的高级封装,如果说事件是用来创建窗口控件的,那么信号与槽就是用来对这个控件进行使用的,比如一个按钮,当我们使用按钮时,只关心clicked信号,至于这个按钮如何接受并处里鼠标点击事件,然后在发射这个信号,则不关心,但是如果要重载一个按钮,这时候就要关心了,比如可以改变它的行为:在鼠标按下时触发clicked信号,而不是释放时 PyQt5常见事件类型 pyqt是对Qt的封装,qt程序是事件驱动的,它的每个动作都有幕后某个事件所触发,Qt事件类型有很多,常见

浅析C# 委托(Delegate)

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针.委托(Delegate) 是存有对某个方法的引用的一种引用类型变量.引用可在运行时被改变. 委托(Delegate)特别用于实现事件和回调方法.所有的委托(Delegate)都派生自 System.Delegate 类. 声明委托(Delegate) 委托声明决定了可由该委托引用的方法.委托可指向一个与其具有相同标签的方法. 例如,假设有一个委托: public delegate int MyDelegate (string

C#中Predicate&lt;T&gt;与Func&lt;T, bool&gt;泛型委托的用法实例

本文以实例形式分析了C#中Predicate<T>与Func<T, bool>泛型委托的用法,分享给大家供大家参考之用.具体如下: 先来看看下面的例子: static void Main(string[] args) { List<string> l = new List<string>(); l.Add("a"); l.Add("b"); l.Add("s"); l.Add("t&quo

C#泛型委托的用法实例分析

本文实例讲述了C#泛型委托的用法.分享给大家供大家参考.具体分析如下: 冒泡排序大家都知道,例如一个整形数组,可以用冒泡排序来使它按从小到大的顺序排序, 但它仅限于了按整形数组来排序,如何做到按任意类型进行排序呢,例如按一个类的某个属性进行排序? 举例说明:定义一组以类MEmployee为元素的数组,按MEmployee的Salary属性进行排序,类似的可以引伸为对其他类型的比较 元素类定义: public class MEmployee { public string Name { get;

thinkphp模板继承实例简述

本文实例讲述了thinkphp模板继承的实现方法.分享给大家供大家参考.具体实现方法如下: 模板的继承: 复制代码 代码如下: <block name='top'></block><!--这是父级模板--> <div style='border:1px solid gray;background:#abcdef;height:100px;'>广告部分</div> <block name='body'></block> &l

Java单例模式实例简述

本文实例讲述了Java的单例模式,是Java程序设计中非常重要的概念.分享给大家供大家参考.具体分析如下: 所谓单子模式就是在整个应用过程中只向外界提供唯一的一份实例,也就是说在应用时只有一个实例,这样也就不用反反复复的创建实例了.那么根据他的要求,看下面一个最简单的单例模式的代码: public class Singleton { private static Singleton single = new Singleton(); private Singleton(){ } public s

thinkphp模板赋值与替换实例简述

本文实例讲述了thinkphp模板赋值与替换方法.分享给大家供大家参考.具体实现方法如下: 1. 模板中的赋值方法: 复制代码 代码如下: $this->assign('name','我们');//第一种赋值方法  $this->name='jb51';//第二种动态赋值方法  $this->display(); 2. 模板替换方法: PUBLIC__:会被替换成当前网站的公共目录,通常是 /thinkphp/Public/;在Public中建立Css文件夹,建立basic.css文件