• 委托类型定义
     C#编译器处理委托时,先自动产生一个派生自System.MulticastDelegate的密封类。这个类与它的基类System.Delegate一起为委托提供必要的基础设施,以维护以后将要调用的方法列表。它含有3个编译器生成的方法,这3个方法的参数与返回值基于委托的声明。
 
public sealed class DelegateName :System.MulticastDelegate
{
    public DReturnType Invoke (DParams);
    public IAsyncResult BeginInvoke(DParams,AsyncCallback cb,object state);
    public DReturnType  EndInvoke(DRefAndOutParams,IAsyncResult result);
}
  • 委托类型实例
    • 某种类型的实例方法和可分配给该类型的目标对象。
    • 某种类型的实例方法(包含在形参表中公开的隐藏 this 参数)。该委托称为开放式实例委托。
    • 静态方法。
    • 静态方法和可分配给该方法的第一个参数的目标对象。该委托称为通过其第一个参数关闭。
  • 何时使用委托而不使用接口
     委托和接口都允许类设计器分离类型声明和实现。给定的接口可由任何类或结构继承和实现;可以为任何类中的方法创建委托,前提是该方法符合委托的方法签名。接口引用或委托可由不了解实现该接口或委托方法的类的对象使用。既然存在这些相似性,那么类设计器何时应使用委托,何时又该使用接口呢?
  • 在以下情况中使用委托
    • 当使用事件设计模式时。
    • 当封装静态方法可取时。
    • 当调用方不需要访问实现该方法的对象中的其他属性、方法或接口时。
    • 需要方便的组合。
    • 当类可能需要该方法的多个实现时。
  • 在以下情况中使用接口
    • 当存在一组可能被调用的相关方法时。
    • 当类只需要方法的单个实现时。
    • 当使用接口的类想要将该接口强制转换为其他接口或类类型时。
    • 当正在实现的方法链接到类的类型或标识时:例如比较方法。
  • 声明委托
     声明一个新的委托类型。每个委托类型都描述参数的数目和类型,以及它可以封装的方法的返回值类型。每当需要一组新的参数类型或新的返回值类型时,都必须声明一个新的委托类型。
public delegate void ProcessBookDelegate(Book book);
  • 实例化委托
     声明了委托类型后,必须创建委托对象并使之与特定方法关联。
     在上面的示例中,这是通过将 PrintTitle 方法传递给ProcessPaperbackBooks 方法来完成的。
bookDB.ProcessPaperbackBooks(PrintTitle);
     这将创建与静态方法 Test.PrintTitle 关联的新委托对象。类似地,对象 totaller 的非静态方法 AddBookToTotal 是按如下方式传递的
bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
  • 调用委托
     创建委托对象后,通常将委托对象传递给将调用该委托的其他代码。通过委托对象的名称(后面跟着要传递给委托的参数,括在括号内)调用委托对象。
processBook(b);
  • 多路广播委托
     本示例演示如何组合多路广播委托。委托对象的一个用途在于,可以使用 + 运算符将它们分配给一个要成为多路广播委托的委托实例。组合的委托可调用组成它的那两个委托。只有相同类型的委托才可以组合。- 运算符可用来从组合的委托移除组件委托。
     根据CIL代码可以发现,+=操作符实际上是转换为了一个静态的Delegate.Combine()方法的调用。-=操作符实际上是转换为了一个静态的Delegate.Remove()方法的调用。
 
delegate void Del(string s);
class TestClass
{
static void Hello(string s)
{
System.Console.WriteLine(" Hello, {0}!", s);
}
static void Goodbye(string s)
{
System.Console.WriteLine(" Goodbye, {0}!", s);
}
static void Main()
{
Del a, b, c, d;
a = Hello;
b = Goodbye;
c = a + b;
d = c - a;
System.Console.WriteLine("Invoking delegate a:");
a("A");
System.Console.WriteLine("Invoking delegate b:");
b("B");
System.Console.WriteLine("Invoking delegate c:");
c("C");
System.Console.WriteLine("Invoking delegate d:");
d("D");
}
}
  • 匿名方法
     在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法。要将代码块传递为委托参数,创建匿名方法则是唯一的方法。
     如果使用匿名方法,则不必创建单独的方法,因此减少了实例化委托所需的编码系统开销。例如,如果创建方法所需的系统开销是不必要的,在委托的位置指定代码块就非常有用。启动新线程即是一个很好的示例。无需为委托创建更多方法,线程类即可创建一个线程并且包含该线程执行的代码。
 
button1.Click += delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };

delegate void Del(int x);
Del d = delegate(int k) { /* ... */ };

void StartThread()
{
System.Threading.Thread t1 = new System.Threading.Thread
(delegate()
{
System.Console.Write("Hello, ");
System.Console.WriteLine("World!");
});
t1.Start();
}

匿名方法可以访问定义他们的方法的本地变量,即匿名方法的外部变量。

    • 匿名方法不能访问定义方法中的ref或out参数
    • 匿名方法中的本地变量不能与外部方法的本地变量重名
    • 匿名方法可以访问外部类作用域中的实例变量或静态变量
  • 委托中的协变和逆变
     将方法签名与委托类型匹配时,协变和逆变为您提供了一定程度的灵活性。
     协变也称为宽松委托,协变允许构造一个委托,指向返回类及其相关继承体系的方法。
     逆变允许方法具有的派生参数类型比委托类型中的更少。
    • 协变
     本示例演示如何将委托与具有返回类型的方法一起使用,这些返回类型派生自委托签名中的返回类型。由 SecondHandler 返回的数据类型是 Dogs 类型,它是由委托中定义的 Mammals 类型派生的。
     Foo<父类> = Foo<子类> 
class Mammals{}
class Dogs : Mammals{}
class Program
{
public delegate Mammals HandlerMethod();
public static Mammals FirstHandler(){return null;}
public static Dogs SecondHandler(){return null;}
static void Main()
{
HandlerMethod handler1 = FirstHandler;
HandlerMethod handler2 = SecondHandler;
}
}
    • 逆变

本示例演示如何将委托与具有某个类型的参数的方法一起使用,这些参数是委托签名参数类型的基类型。通过逆变,以前必须使用若干个不同处理程序的地方现在只要使用一个事件处理程序即可
。如,现在可以创建一个接收 EventArgs 输入参数的事件处理程序,然后,可以将该处理程序与发送 MouseEventArgs 类型(作为参数)的 Button.MouseClick 事件一起使用,也可以将该处理程序与发送 KeyEventArgs 参数的 TextBox.KeyDown 事件一起使用。

     Foo<子类> = Foo<父类>
System.DateTime lastActivity;
public Form1()
{
InitializeComponent();
lastActivity = new System.DateTime();
this.textBox1.KeyDown += this.MultiHandler;
this.button1.MouseClick += this.MultiHandler;
}
private void MultiHandler(object sender, System.EventArgs e)
{
lastActivity = System.DateTime.Now;
}
  • 泛型委托
     委托可以定义自己的类型参数。引用泛型委托的代码可以指定类型参数以创建已关闭的构造类型,就像实例化泛型类或调用泛型方法一样。
public delegate void Del<T>(T item);
public static void Notify(int i) { }
Del<int> m1 = new Del<int>(Notify);
  • 方法组转换
      C#提供了一种叫做方法组转换的简便方法,该特性允许我们在调用以委托作为参数的方法时直接提供方法的名称,而不用创建委托对象,此功能适用于具体委托类型和泛型委托类型,并使您可以使用如下简化的语法写入上一行
Del<int> m2 = Notify;
  • 泛型类内部定义委托使用泛型类类型参数
     在泛型类内部定义的委托使用泛型类类型参数的方式可以与类方法所使用的方式相同。
class Stack<T>
{
T[] items;
int index;
public delegate void StackDelegate(T[] items);
}
  • 引用委托的代码必须指定包含类的类型变量
private static void DoWork(float[] items) { }
public static void TestStack()
{
Stack<float> s = new Stack<float>();
Stack<float>.StackDelegate d = DoWork;
}
  • 泛型委托取代传统装箱拆箱委托
     根据典型设计模式定义事件时,泛型委托尤其有用,因为发送方参数可以为强类型,不再需要强制转换成 Object,或反向强制转换。
delegate void StackEventHandler<T, U>(T sender, U eventArgs);
class Stack<T>
{
public class StackEventArgs : System.EventArgs { }
public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent;
protected virtual void OnStackChanged(StackEventArgs a)
{
stackEvent(this, a);
}
}
class SampleClass
{
public void HandleStackChange<T>(Stack<T> stack, Stack<T>.StackEventArgs args) { }
}
public static void Test()
{
Stack<double> s = new Stack<double>();
SampleClass o = new SampleClass();
s.stackEvent += o.HandleStackChange;
}

.NET 委托的更多相关文章

  1. HTML 事件(三) 事件流与事件委托

    本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...

  2. C#基础篇 - 理解委托和事件

    1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...

  3. [.NET] C# 知识回顾 - 委托 delegate (续)

    C# 知识回顾 - 委托 delegate (续) [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6046171.html 序 上篇<C# 知识回 ...

  4. [C#] C# 知识回顾 - 委托 delegate

    C# 知识回顾 - 委托 delegate [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6031892.html 目录 What's 委托 委托的属性 ...

  5. 9、委托、事件、Lambda

    开始 关于委托,肯定是要有问题的. 第一个问题,委托用来干什么? 看.net中的表述:在.net平台下,委托类型用来定义和相应应用程序中的回调.(回调?处理内存中两个实体双向通信的一种技术.)   第 ...

  6. iOS 委托与文本输入(内容根据iOS编程编写)

    文本框(UITextField) 本章节继续编辑 JXHypnoNerd .文件地址 . 首先我们继续编辑  JXHypnosisViewController.m 修改  loadView 方法,向  ...

  7. C#委托异步调用

    参考页面: http://www.yuanjiaocheng.net/webapi/mvc-consume-webapi-get.html http://www.yuanjiaocheng.net/w ...

  8. JavaScript事件代理和委托(Delegation)

    JavaScript事件代理 首先介绍一下JavaScript的事件代理.事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委 ...

  9. .NET面试题系列[7] - 委托与事件

    委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...

  10. .NET基础拾遗(4)委托、事件、反射与特性

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

随机推荐

  1. 【六年开源路】FineUI家族今日全部更新!

      FineUI(开源版) 基于 ExtJS 的开源 ASP.NET 控件库 FineUI的使命 创建 No JavaScript,No CSS,No UpdatePanel,No ViewState ...

  2. 奇虎360诉腾讯QQ垄断案之我见(3Q大战之我见)

    这两款软件我都在用,要说时间最长感情最深的应该是腾讯QQ,1999年诞生的那年就在用QQ了! 不过感情归感情,个人看法归个人看法,不能用感情来判断. 正所谓外行看热闹,内行看门道.从事实上讲在使用这两 ...

  3. bzoj1189

    1189: [HNOI2007]紧急疏散evacuate Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2321  Solved: 724[Submi ...

  4. nginx平滑升级

    1.查询原来安装配置信息 [root@t-scrmap1-v-szzb local]# netstat -unlatp | grep nginx tcp 0 0 0.0.0.0:80 0.0.0.0: ...

  5. cocos2d-x打飞机实例总结(一):程序入口分析和AppDelegate,Application,ApplicationProtocol三个类的分析

    首先,是个敲代码的,基本上都知道程序的入口是main函数,显然,就算在cocos2d-x框架中也一样 我们看看main函数做了什么 #include "main.h" #inclu ...

  6. WKWebView与JS交互,UIWebView+JavascriptCore和JS交互

    最近一直在做有关Swift和JavaScript交互的程序,所以有关UIWebView和WKWebView在使用上的差别在此总结下: UIWebView: (1)创建 var webView: UIW ...

  7. C#环境

  8. SDL简介(网络汇总)

    摄像头视频播放采用sdl,下面简单介绍下.不保证正确及网址永远有效.后面文章采用tao框架http://sourceforge.net/projects/taoframework/      SDL. ...

  9. 创建NetWorkDataset---Shapefile篇

    部分参照esri的官方例子,理解下各个参数,对照自己的NetWorkDatase创建方式(在arcmap中),多试试代码就调好了. /// <summary> /// 创建NetWorkD ...

  10. C# 调用WebService的3种方式 :直接调用、根据wsdl生成webservice的.cs文件及生成dll调用、动态调用

    1.直接调用 已知webservice路径,则可以直接 添加服务引用--高级--添加web引用 直接输入webservice URL.这个比较常见也很简单 即有完整的webservice文件目录如下图 ...