• 委托类型定义
     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. 地理信息系统 - ArcGIS - 高/低聚类分析工具(High/Low Clustering ---Getis-Ord General G)

    前段时间在学习空间统计相关的知识,于是把ArcGIS里Spatial Statistics工具箱里的工具好好研究了一遍,同时也整理了一些笔记上传分享.这一篇先聊一些基础概念,工具介绍篇随后上传. 空间 ...

  2. Css-深入学习之单个颜色实现 hover 和 active 时的明暗变化效果

    本文是作者从别的网站和文章学习了解的知识,简单做了个笔记,想要学习更多的可以参考这里:[css进阶]伪元素的妙用--单标签之美,奇思妙想 (1.normal)(2.hover)(3.active) / ...

  3. 如何理解vue.js组件的作用域是独立的

    vue.js组件的作用域是独立,可以从以下三个方面理解: 1.父组件模板在父组件作用域内编译,父组件模板的数据用父组件内data数据:2.子组件模板在子组件作用域内编译,子组件模板的数据用子组件内da ...

  4. 二维码生成Zxing.net DEMO

    Zxing.net是google维护的一个开源项目.用于在.net平台上生成二维码等,当然还有更多其他用途. 用nuget安装命令 install-package zxing.net 然后添加命名空间 ...

  5. 发布ASP.NET Core网站到IIS

    打开VS2015,新建项目: 选择模板,取消身份验证: 项目加载完成后就可以运行了: 下面要发布到IIS: 需要安装从IIS到Kestrel server的反向代理,下载地址:.NET Core Wi ...

  6. 在渲染前获取 View 的宽高

    在渲染前获取 View 的宽高 这是一个比较有意义的问题,或者说有难度的问题,问题的背景为:有时候我们需要在view渲染前去获取其宽高,典型的情形是,我们想在onCreate.onStart.onRe ...

  7. Servlet和JSP学习指导与实践(二):Session追踪

    前言: web应用中经常需要对某些有用的信息进行存储或者附加一些信息.本文主要介绍session,即“会话”跟踪的几种不同方式~ ----------------------------4种管理ses ...

  8. C++ 顺序容器基础知识总结

    0.前言 本文简单地总结了STL的顺序容器的知识点.文中并不涉及具体的实现技巧,对于细节的东西也没有提及.一来不同的标准库有着不同的实现,二来关于具体实现<STL源码剖析>已经展示得全面细 ...

  9. JavaScript中URL的解码和编码

    这些URI方法encodeURI.encodeURIComponent().decodeURI().decodeURIComponent()代替了BOM的escape()和unescape()方法. ...

  10. Day3-python基础3

    本次学习内容 元组 字典 集合 字符编码 文件处理 一.元组 定义:与列表类似,定义是使用() 特性: 1.可存放多个值 2.元组里的元素是不可变的 3.有序,下标从0开始从左往右的顺序访问 元组常用 ...