前言

委托其实一直以来都感觉自己应该挺熟悉的,直到最近又去翻了翻 CLR via C#,感觉我之前的理解可能还有失偏颇。在这记录一下。

之前文章的链接:

委托(Delegate)

说起委托,我们首先应该想到 回调函数 .NET-Framework 通过委托 来提供回调函数机制。委托确保回调方法是类型安全的,委托还允许调用多个方法,并支持静态方法和实例方法。

下面通过一个具体实例来分析委托的声明,创建,以及使用。

public sealed class Program {
public static void Main() {
DelegateIntro.Go();
GetInvocationList.Go();
AnonymousMethods.Go();
DelegateReflection.Go("TwoInt32s", "Add", "123", "321");
DelegateReflection.Go("TwoInt32s", "Subtract", "123", "321");
DelegateReflection.Go("OneString", "NumChars", "Hello there");
DelegateReflection.Go("OneString", "Reverse", "Hello there");
}
} internal sealed class DelegateIntro {
// Declare a delegate type; instances refer to a method that
// takes an Int32 parameter and returns void.
internal delegate void Feedback(Int32 value); public static void Go() {
StaticDelegateDemo();
InstanceDelegateDemo();
ChainDelegateDemo1(new DelegateIntro());
ChainDelegateDemo2(new DelegateIntro());
} private static void StaticDelegateDemo() {
Console.WriteLine("----- Static Delegate Demo -----");
Counter(1, 3, null);
Counter(1, 3, new Feedback(DelegateIntro.FeedbackToConsole));
Counter(1, 3, new Feedback(FeedbackToMsgBox)); // "Program." is optional
Console.WriteLine();
} private static void InstanceDelegateDemo() {
Console.WriteLine("----- Instance Delegate Demo -----");
DelegateIntro di = new DelegateIntro();
Counter(1, 3, new Feedback(di.FeedbackToFile)); Console.WriteLine();
} private static void ChainDelegateDemo1(DelegateIntro di) {
Console.WriteLine("----- Chain Delegate Demo 1 -----");
Feedback fb1 = new Feedback(FeedbackToConsole);
Feedback fb2 = new Feedback(FeedbackToMsgBox);
Feedback fb3 = new Feedback(di.FeedbackToFile); Feedback fbChain = null;
fbChain = (Feedback)Delegate.Combine(fbChain, fb1);
fbChain = (Feedback)Delegate.Combine(fbChain, fb2);
fbChain = (Feedback)Delegate.Combine(fbChain, fb3);
Counter(1, 2, fbChain); Console.WriteLine();
fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox));
Counter(1, 2, fbChain);
} private static void ChainDelegateDemo2(DelegateIntro di) {
Console.WriteLine("----- Chain Delegate Demo 2 -----");
Feedback fb1 = new Feedback(FeedbackToConsole);
Feedback fb2 = new Feedback(FeedbackToMsgBox);
Feedback fb3 = new Feedback(di.FeedbackToFile); Feedback fbChain = null;
fbChain += fb1;
fbChain += fb2;
fbChain += fb3;
Counter(1, 2, fbChain); Console.WriteLine();
fbChain -= new Feedback(FeedbackToMsgBox);
Counter(1, 2, fbChain);
} private static void Counter(Int32 from, Int32 to, Feedback fb) {
for (Int32 val = from; val <= to; val++) {
// If any callbacks are specified, call them
if (fb != null)
fb(val);
}
} private static void FeedbackToConsole(Int32 value) {
Console.WriteLine("Item=" + value);
} private static void FeedbackToMsgBox(Int32 value) {
MessageBox.Show("Item=" + value);
} private void FeedbackToFile(Int32 value) {
StreamWriter sw = new StreamWriter("Status", true);
sw.WriteLine("Item=" + value);
sw.Close();
}
}

在上面,Feedback 委托指定的方法接受Int32参数,返回 void。

将方法绑定到委托时,C#和CLR都允许引用类型的协变性(covariance)和逆变性(contravariance)。协变性是指方法能返回从委托的返回类型派生的一个类型。逆变性是指方法获取的参数可以是委托的参数类型的基类。关于可变性和逆变性可以参考一下之前的博文接口和委托的泛型可变性

假设定义如下委托

delegate Object MyCallback(FilStram s);

完全可以构造如下的方法:

String SomeMethod(Stream s);

SomeMethod 的返回类型(String) 派生自委托的返回类型(Object)这种协变性是允许的。SomeMethod 的参数类型(Stream)是委托的参数类型(FileStream)的基类。这种逆变性是允许的。

只有引用类型才支持协变性和逆变性,值类型或void 不支持

委托揭秘

首先让我们再来看一下这个委托的声明代码:

internal delegate void Feedback(Int32 value);

编译器会做这样一件事,把上面那段代码定义成下面这一段

internal class Feedback :System.MulticastDelegate{
//构造器
public Feedback(Object @object, Intptr method); public virtual void Invoke(Int32 value); public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object @object); public virtual void EndInvoke(IAsyncResult result);
}

Feedback 类派生自FCL(Framework class library)定义的 System.MulticastDelegate 类

所有委托类型都派生自MulticastDelegate

委托类既可以嵌套在一个类型中定义,也可以在全局范围中定义。简单来说,由于委托是类,所以凡是能够定义类的地方,都能定义委托。

接下来介绍一下MulticastDelegate 中三个重要的非公共字段

  1. _target 类型为 System.Object ,当委托队形包装一个静态方法时,这个字段为null。当委托对象包装一个实例方法时,这个字段引用的是回调方法要操作的对象。换言之,这个字段指出要传给实例方法的隐式参数this 的值。
  2. _methodPtr 类型为 System.IntPtr 一个内部的整数值,CLR 用它来标识要回调的方法
  3. _invocationList 类型为 System.Object 该字段通常为 null。构造委托链时它引用一个委托数组

比如上面代码中的

Feedback fb1 = new Feedback(FeedbackToConsole)

那么这个 fb1 的状态就如下面这张图这样:

下面贴上原书中委托链的状态图(偷个懒,太难画了),

下面给出一个我本地委托测试的小Demo

Driver.cs

using System;

namespace EventDemo.CarDemo {
public class Driver {
public string Name { get; set; } public void DriveCar () {
Console.WriteLine ($"I am driver:{Name}");
}
}
}

Passenger.cs

namespace EventDemo.CarDemo {
public class Passenger { public string Name { get; set; } public void BoardCar () {
System.Console.WriteLine ($"我上车了,我是:{Name}");
}
}
}

MyCar.cs

using System;

namespace EventDemo.CarDemo {

    public delegate void CarHandler ();

    public class MyCar {
//定义一个 上车 委托方法的事件
public event CarHandler CarNumberNotification; public void RunCar () {
Console.WriteLine ($"好的,准备上车了!");
if (CarNumberNotification != null) {
CarNumberNotification ();
}
}
}
}

Program.cs

 //纯委托版本
CarHandler carHandler = null;
carHandler += driver.DriveCar;
carHandler += passenger.BoardCar;
carHandler.Invoke();

深入理解委托(Delegate)的更多相关文章

  1. 理解委托(delegate)及为什么要使用委托

    理解委托(delegate)及为什么要使用委托 委托:是一种定义方法签名的类型. 当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联. 您可以通过委托实例调用方法. 上述为官方说法,理解起来 ...

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

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

  3. C# 委托Delegate(一) 基础介绍&用法

    本文是根据书本&网络 前人总结的. 1. 前言 定义&介绍: 委托Delegate是一个类,定义了方法的类型, 使得可以将方法当做另一个方法的参数来进行传递,这种将方法动态地赋给参数的 ...

  4. C#基础知识六之委托(delegate、Action、Func、predicate)

    1. 什么是委托 官方解释 委托是定义方法签名的类型,当实例化委托时,您可以将其实例化与任何具有兼容签名的方法想关联,可以通过委托实例调用方法. 个人理解 委托通俗一点说就是把一件事情交给别人来帮助完 ...

  5. C#学习之初步理解委托、事件、匿名方法和Lambda

    最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对 ...

  6. c# 委托 delegate

    委托是一种存储函数引用的类型,在事件和事件的处理时有重要的用途 通俗的说,委托是一个可以引用方法的类型,当创建一个委托,也就创建一个引用方法的变量,进而就可以调用那个方法,即委托可以调用它所指的方法. ...

  7. Unity 项目中委托Delegate用法案例

    Unity中Delegate的用法场景 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar - ...

  8. 关于C# 委托(delegate)与事件(event)的用法及事例

    C#中的委托和事件对于新手可能会有一点难理解,所以先从一个小例子入手,以便能更好的理解其如何使用.有一个学生每天定闹钟在早上6点起床,所以当每天早上6点的时候,闹钟就会响起来,从而学生才会按时起床. ...

  9. 编写高质量代码改善C#程序的157个建议——建议44:理解委托中的协变

    建议44:理解委托中的协变 委托中的泛型变量天然是部分支持协变的.为什么是“部分支持协变”?看下面示例: class Program { public delegate T GetEmployeeHa ...

随机推荐

  1. oracle导入dmp文件的2种方法

    使用imp.impdp方式导入数据 1.使用imp导入数据 打开cmd窗口,然后直接敲入一下命令即可,需要注意的是,要事先把dmp文件放到正确的路径中去 imp yx_base/@yx_192. fi ...

  2. 201621123062《java程序设计》第13周作业总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 思维导图: 2. 为你的系统增加网络功能(购物车.图书馆管理.斗地主等)-分组完成 为了让你的系统可以被多 ...

  3. 关于python词典(Dictionary)的get()用法

    先贴出参考链接:http://www.runoob.com/python/att-dictionary-get.html get()方法语法: dict.get(key, default=None) ...

  4. C语言:第0次作业

    问题1: 你为什么选择计算机专业?你认为你的条件如何?和这些博主比呢? 感性地讲,高中时意外看到了电影<社交网络>,自那时起就将将马克扎克伯格视为偶像,他天才的智慧和长远的眼光深深吸引了我 ...

  5. python 一致性哈希 分布式

    hash_ring # -*- coding: utf-8 -*- """ hash_ring ~~~~~~~~~~~~~~ Implements consistent ...

  6. iOS开发UIKit框架-可视化编程-XIB

    1. Interface Builder 可视化编程 1> 概述 GUI : 图形用户界面(Graphical User Interface, 简称GUI, 又称图形化界面) 是指采用图形方式显 ...

  7. Linux系统安装gcc/g++详细过程

    下载: http://ftp.gnu.org/gnu/gcc/gcc-4.5.1/gcc-4.5.1.tar.bz2 浏览: http://ftp.gnu.org/gnu/gcc/gcc-4.5.1/ ...

  8. 多种在线地图综合对比,Google,必应,arcgis Online...

    不同网络地图的对比 天地图 坐标系:WGS84 地图配色:   POI数量:丰富      有无建筑:有 地图特点:天地图按照国家标准进行配图,道路.水系.植被等图层用对应颜色渲染, POI信息丰富, ...

  9. 13-TypeScript单例模式

    在JavaScript中,要实现设计模式比较复杂.而在TypeScript中因为使用面向对象的思想编程,要实现设计模式的方式与后端语言C#.Java等非常类似. 单例模式是一种常用的设计模式,通常用于 ...

  10. js常用API方法

    String对象常用的API:API指应用程序编程接口,实际上就是一些提前预设好的方法. charAt() 方法可返回指定位置的字符. stringObject.charAt(index) index ...