委托
1 请解释委托的基本原理
2 委托回调静态方法和实例方法有何区别
3 什么是链式委托
4 链式委托的执行顺序是怎么样的
5 可否定义拥有返回值的方法的委托链
6 委托通常可以应用在哪些场合

委托
1 请解释委托的基本原理

委托是一个钟数据类型,用来传递方法。委托类型继承自System.Delegate,自定义委托类型都直接继承自System.NulticastDelegate,System.NulticastDelegate又继承自System.Delegate。每个委托至少包含一个指向某个方法的指针,该方法可以是实例方法,也可以是静态方法。委托实现了回调方法的机制,能够帮助程序员设计出更加简洁优美的面向对象程序。

示例:

    class SimpleDelegate
{
/// <summary>
/// 定义的委托。
/// </summary>
/// <param name="i">接受一个整型参数</i>
public delegate void TestDelegate(int i);
static void Main(string[] args)
{
//调用委托方法
TestDelegate d = new TestDelegate(PrintMessage1);
//或者直接写TestDelegate d = PrintMessage1;
d();
d();
Console.Read();
}
/// <summary>
/// 一个静态方法,符合TestDelegate的定义
/// </summary>
/// <param name="i">整型参数</param>
static void PrintMessage1(int i)
{
Console.WriteLine("第" + i + "个方法");
}
}

输出:

第0个方法
第1个方法

本质上,委托的调用就是执行了定义在委托所生成的Invoke方法。

完全可以这样调用:

d.Invoke(0);
d.Invoke(1);

2 委托回调静态方法和实例方法有何区别

当一个实例方法被调用时,需要通过实例对象来访问,绑定一个实例方法到委托必须同时让委托得到实例的方法的代码段和实例对象的信息,这样委托在被回调的时候.NET才能成功地执行实例方法。

用Reflector查看的部分System.Delegate代码:

_target是一个指向目标实例的引用。当绑定一个实例方法给委托时,该参数会被设置为该方法所在类型的一个实例对象。而当绑定一个静态方法给委托时,该参数则被设置为null。

_mathodPtr是一个指向绑定方法代码段的指针。绑定静态方法还是实例方法在这个成员的设置上并没有不同。

3 什么是链式委托

也叫多播委托。所有自定义委托都直接继承自System.MulticastDelegate类型。这个类型即是为链表委托而设计的。

链式委托是指一个由委托串成的链表,当链表中的一个委托被回调时,所有链表上该委托的后续委托都将会被顺序执行。

示例:

    class MulticastDelegate
{
/// <summary>
/// 定义的委托。
/// </summary>
public delegate void TestMultiDelegate();
static void Main(string[] args)
{
//申明一个委托变量,并绑定第一个方法
TestMultiDelegate handler = PrintMessage1;
//绑定第二个方法
handler += PrintMessage2;
//绑定第三个方法
handler += PrintMessage3;
//检查结果
handler();
Console.Read();
}
static void PrintMessage1()
{
Console.WriteLine("第一个方法");
}
static void PrintMessage2()
{
Console.WriteLine("第二个方法");
}
static void PrintMessage3()
{
Console.WriteLine("第三个方法");
}
}

输出:

第一个方法
第二个方法
第三个方法

用Reflector查看编译后的Main方法:

private static void Main(string[] args)
{
TestMultiDelegate handler = new TestMultiDelegate(Program.PrintMessage1);
handler = (TestMultiDelegate) Delegate.Combine(handler, new TestMultiDelegate(Program.PrintMessage2));
handler = (TestMultiDelegate) Delegate.Combine(handler, new TestMultiDelegate(Program.PrintMessage3));
handler();
Console.Read();
}

调用Delegate.Combine方法 详细内容可查看GitHub中Delegate源码

        public static Delegate Combine(Delegate a, Delegate b)
{
if ((Object)a == null) // cast to object for a more efficient test
return b; return a.CombineImpl(b);
}

在调用CombineImpl方法

        protected virtual Delegate CombineImpl(Delegate d)
{
throw new MulticastNotSupportedException(Environment.GetResourceString("Multicast_Combine"));
}

MulticastDelegate重写了这个方法:详细内容可查看GitHub中MulticastDelegate源码

         // This method will combine this delegate with the passed delegate
// to form a new delegate.
[System.Security.SecuritySafeCritical] // auto-generated
protected override sealed Delegate CombineImpl(Delegate follow)
{
if ((Object)follow == null) // cast to object for a more efficient test
return this; // Verify that the types are the same...
if (!InternalEqualTypes(this, follow))
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis")); MulticastDelegate dFollow = (MulticastDelegate)follow;
Object[] resultList;
int followCount = ;
Object[] followList = dFollow._invocationList as Object[];
if (followList != null)
followCount = (int)dFollow._invocationCount; int resultCount;
Object[] invocationList = _invocationList as Object[];
if (invocationList == null)
{
resultCount = + followCount;
resultList = new Object[resultCount];
resultList[] = this;
if (followList == null)
{
resultList[] = dFollow;
}
else
{
for (int i = ; i < followCount; i++)
resultList[ + i] = followList[i];
}
return NewMulticastDelegate(resultList, resultCount);
}
else
{
int invocationCount = (int)_invocationCount;
resultCount = invocationCount + followCount;
resultList = null;
if (resultCount <= invocationList.Length)
{
resultList = invocationList;
if (followList == null)
{
if (!TrySetSlot(resultList, invocationCount, dFollow))
resultList = null;
}
else
{
for (int i = ; i < followCount; i++)
{
if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
{
resultList = null;
break;
}
}
}
} if (resultList == null)
{
int allocCount = invocationList.Length;
while (allocCount < resultCount)
allocCount *= ; resultList = new Object[allocCount]; for (int i = ; i < invocationCount; i++)
resultList[i] = invocationList[i]; if (followList == null)
{
resultList[invocationCount] = dFollow;
}
else
{
for (int i = ; i < followCount; i++)
resultList[invocationCount + i] = followList[i];
}
}
return NewMulticastDelegate(resultList, resultCount, true);
}
}

顺着内核的源码就可以一步步看到多播委托是如何执行的了。

4 链式委托的执行顺序是怎么样的

按照委托链上的顺序从当前委托开始依次往后执行,如果有需要,可通过GetInvocationList()方法来获得委托链上所有需要执行的委托,并且按照任何希望的顺序执行它们。

5 可否定义拥有返回值的方法的委托链

委托可以是带有返回值的方法,但多于一个带返回值的方法被添加到委托链中时,程序员需要手动调用委托链上的每一个方法,否则委托使用者只能得到委托链上最后一个被执行方法的返回值。

示例:

    public delegate String GetStringDelegate();

    class DelegateReturn
{
static void Main(string[] args)
{
//GetSelfDefinedString最后被添加
GetStringDelegate _myDelegate1 = new GetStringDelegate(GetTimeString);
_myDelegate1 += GetTypeName;
_myDelegate1 += GetSelfDefinedString;
Console.WriteLine(_myDelegate1()); //GetTimeString被最后添加
GetStringDelegate _myDelegate2 = new GetStringDelegate(GetSelfDefinedString);
_myDelegate2 += GetTypeName;
_myDelegate2 += GetTimeString;
Console.WriteLine(_myDelegate2()); //GetTypeName被最后添加
GetStringDelegate _myDelegate3 = new GetStringDelegate(GetSelfDefinedString);
_myDelegate3 += GetTimeString;
_myDelegate3 += GetTypeName;
Console.WriteLine(_myDelegate3()); Console.WriteLine(); GetStringDelegate _myDelegate4 = GetTimeString;
_myDelegate4 += GetTypeName;
_myDelegate4 += GetSelfDefinedString;
foreach (Delegate d in _myDelegate4.GetInvocationList())
{
Console.WriteLine(d.DynamicInvoke());
}
Console.Read();
}
static String GetTimeString()
{
return DateTime.Now.ToString();
}
static String GetTypeName()
{
return typeof(DelegateReturn).ToString();
}
static String GetSelfDefinedString()
{
return "我是字符串。";
}
}

输出:

我是字符串。
2015/9/10 22:05:52
MyTest.DelegateReturn

2015/9/10 22:05:52
MyTest.DelegateReturn
我是字符串。

6 委托通常可以应用在哪些场合

委托的应用场合通常是任务的执行者把细节工作进行再分配,执行者确切地知道什么工作将被执行,但把执行细节委托给其他组件、方法或者程序集。

委托比接口更加灵活,知道返回值类型和参数类型就可以定义一个委托。

举一个日志读写的例子:

日志子系统的使用者所有希望的都以单一的方法,传入日志内容和类型,而日志系统会根据具体情况来进行日志动作。对于日志系统的设计者来说,写一条日志可能需要包含一系列的工作,而日志系统决定把这些工作进行适当的分派,这时就需要一个委托成员。下面是简单的实现方式:

首先定义日志类型使用的书写日志委托类型,并且定义代表日志类别的枚举类型:

    /// <summary>
/// Log的类别
/// </summary>
public enum LogType
{
Debug,
Trace,
Info,
Warn,
Error
}
/// <summary>
/// Log委托类型,由日志使用者直接执行来完成写日志的工作
/// </summary>
/// <param name="content">日志内容</param>
/// <param name="type">日志类别</param>
public delegate void Log(String content,LogType type);

定义日志管理类型的基本成员、构造方法、析构方法:

     /// <summary>
/// 日志管理类型,基本成员和构造方法
/// </summary>
public sealed partial class LogManager:IDisposable
{
private Type _componentType;
private String _logfile;
private FileStream _fs;
public Log WriteLog; //用来写日志的委托
//锁
private static object mutext = new object();
//严格控制无参的构造方法
private LogManager()
{
WriteLog = new Log(PrepareLogFile);
WriteLog += OpenStream; //打开流
WriteLog += AppendLocalTime; //添加本地时间
WriteLog += AppendSeperator; //添加分隔符
WriteLog += AppendComponentType;//添加模块类别
WriteLog += AppendSeperator; //添加分隔符
WriteLog += AppendType; //添加日志类别
WriteLog += AppendSeperator; //添加分隔符
WriteLog += AppendContent; //添加内容
WriteLog += AppendNewLine; //添加回车
WriteLog += CloseStream; //关闭流
}
/// <summary>
/// 构造方法
/// </summary>
/// <param name="type">使用该日志的类型</param>
/// <param name="file">日志文件全路径</param>
public LogManager(Type type, String file):this()
{
_logfile = file;
_componentType = type; }
/// <summary>
/// 释放FileStream对象
/// </summary>
public void Dispose()
{
if (_fs != null)
_fs.Dispose();
GC.SuppressFinalize(this);
}
~LogManager()
{
if (_fs != null)
_fs.Dispose();
} }

委托链上的方法:

     /// <summary>
/// 委托链上的方法(和日志文件有关的操作)
/// </summary>
public sealed partial class LogManager:IDisposable
{
/// <summary>
/// 如果日志文件不存在,则新建日志文件
/// </summary>
private void PrepareLogFile(String content, LogType type)
{
//只允许单线程创建日志文件
lock(mutext)
{
if (!File.Exists(_logfile))
using (FileStream fs = File.Create(_logfile))
{ }
}
}
/// <summary>
/// 打开文件流
/// </summary>
private void OpenStream(String content, LogType type)
{
_fs = File.Open(_logfile, FileMode.Append);
}
/// <summary>
/// 关闭文件流
/// </summary>
private void CloseStream(String content, LogType type)
{
_fs.Close();
_fs.Dispose();
}
}
/// <summary>
/// 委托链上的方法(和日志时间有关的操作)
/// </summary>
public sealed partial class LogManager : IDisposable
{
/// <summary>
/// 为日志添加当前UTC时间
/// </summary>
private void AppendUTCTime(String content, LogType type)
{
String time=DateTime.Now.ToUniversalTime().ToString();
Byte[] con = Encoding.Default.GetBytes(time);
_fs.Write(con, , con.Length);
}
/// <summary>
/// 为日志添加本地时间
/// </summary>
private void AppendLocalTime(String content, LogType type)
{
String time = DateTime.Now.ToLocalTime().ToString();
Byte[] con = Encoding.Default.GetBytes(time);
_fs.Write(con, , con.Length);
}
}
/// <summary>
/// 委托链上的方法(和日志内容有关的操作)
/// </summary>
public sealed partial class LogManager : IDisposable
{
/// <summary>
/// 添加日志内容
/// </summary>
private void AppendContent(String content, LogType type)
{
Byte[] con = Encoding.Default.GetBytes(content);
_fs.Write(con, , con.Length);
}
/// <summary>
/// 为日志添加组件类型
/// </summary>
private void AppendComponentType(String content, LogType type)
{
Byte[] con = Encoding.Default.GetBytes(type.ToString());
_fs.Write(con, , con.Length);
}
/// <summary>
/// 添加日志类型
/// </summary>
private void AppendType(String content, LogType type)
{
String typestring = String.Empty;
switch (type)
{
case LogType.Debug:
typestring = "Debug";
break;
case LogType.Error:
typestring = "Error";
break;
case LogType.Info:
typestring = "Info";
break;
case LogType.Trace:
typestring = "Trace";
break;
case LogType.Warn:
typestring = "Warn";
break;
default:
typestring = "";
break;
}
Byte[] con = Encoding.Default.GetBytes(typestring);
_fs.Write(con, , con.Length);
}
}
/// <summary>
/// 委托链上的方法(和日志的格式控制有关的操作)
/// </summary>
public sealed partial class LogManager : IDisposable
{ /// <summary>
/// 添加分隔符
/// </summary>
private void AppendSeperator(String content, LogType type)
{
Byte[] con = Encoding.Default.GetBytes(" | ");
_fs.Write(con, , con.Length);
}
/// <summary>
/// 添加换行符
/// </summary>
private void AppendNewLine(String content, LogType type)
{
Byte[] con = Encoding.Default.GetBytes("\r\n");
_fs.Write(con, , con.Length);
}
}
/// <summary>
/// 修改所使用的时间类型
/// </summary>
public sealed partial class LogManager : IDisposable
{
/// <summary>
/// 设置使用UTC时间
/// </summary>
public void UseUTCTime()
{
WriteLog = new Log(PrepareLogFile);
WriteLog += OpenStream;
WriteLog += AppendUTCTime;
WriteLog += AppendSeperator;
WriteLog += AppendComponentType;
WriteLog += AppendSeperator;
WriteLog += AppendType;
WriteLog += AppendSeperator;
WriteLog += AppendContent;
WriteLog += AppendNewLine;
WriteLog += CloseStream;
}
/// <summary>
/// 设置使用本地时间
/// </summary>
public void UseLocalTime()
{
WriteLog = new Log(PrepareLogFile);
WriteLog += OpenStream;
WriteLog += AppendLocalTime;
WriteLog += AppendSeperator;
WriteLog += AppendComponentType;
WriteLog += AppendSeperator;
WriteLog += AppendType;
WriteLog += AppendSeperator;
WriteLog += AppendContent;
WriteLog += AppendNewLine;
WriteLog += CloseStream;
}
}

调用:

    class UseLog
{
/// <summary>
/// 使用日志管理类型来记录日志
/// </summary>
static void Main(string[] args)
{
//使用日志
using(LogManager logmanager=
new LogManager(Type.GetType("NET.MST.Sixth.DelegateLog.UseLog"),"C:\\TestLog.txt"))
{
logmanager.WriteLog("新建了日志", LogType.Debug);
logmanager.WriteLog("写数据", LogType.Debug);
logmanager.UseUTCTime();
logmanager.WriteLog("现在是UTC时间", LogType.Debug);
logmanager.UseLocalTime();
logmanager.WriteLog("回到本地时间", LogType.Debug);
logmanager.WriteLog("发生错误", LogType.Error);
logmanager.WriteLog("准备退出", LogType.Info);
}
}
}

日志文件输出:

2015/9/10 22:41:14 | Debug | Debug | 新建了日志
2015/9/10 22:43:56 | Debug | Debug | 写数据
2015/9/10 14:43:56 | Debug | Debug | 现在是UTC时间
2015/9/10 22:43:56 | Debug | Debug | 回到本地时间
2015/9/10 22:43:56 | Error | Error | 发生错误
2015/9/10 22:43:56 | Info | Info | 准备退出

转载请注明出处:

作者:JesseLZJ
出处:http://jesselzj.cnblogs.com

.NET基础 (15)委托的更多相关文章

  1. C#基础系列——委托和设计模式(二)

    前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...

  2. [.net 面向对象编程基础] (15) 抽象类

    [.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义 ...

  3. [.net 面向对象编程基础] (21) 委托

    [.net 面向对象编程基础] (20)  委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1. 什么是委托? 学习委托,我想说,学会了就感觉简 ...

  4. 转载 【.NET基础】--委托、事件、线程(2) https://www.cnblogs.com/chengzish/p/4569912.html

    [.NET基础]--委托.事件.线程(2)   本文介绍event的使用以及原理,本文接上一篇文章的Demo继续[下载上一篇Demo] 上一篇我们在类(dg_SayHi.cs)里面定义代理了4个Del ...

  5. 转载 【.NET基础】--委托、事件、线程(1) https://www.cnblogs.com/chengzish/p/4559268.html

    [.NET基础]--委托.事件.线程(1)   1,委托 是存放方法的指针的清单,也就是装方法的容器 A, 新建winform项目[01委托],项目中添加dg_SayHi.cs 委托类 用于存储方法 ...

  6. 十五. Python基础(15)--内置函数-1

    十五. Python基础(15)--内置函数-1 1 ● eval(), exec(), compile() 执行字符串数据类型的python代码 检测#import os 'import' in c ...

  7. C#基础系列——委托实现简单设计模式

    前言:上一篇介绍了下多线程的相关知识:C#基础系列——多线程的常见用法详解,里面就提到了委托变量.这篇简单介绍下委托的使用.当然啦,园子里面很多介绍委托的文章都会说道:委托和事件的概念就像一道坎,过了 ...

  8. [C#基础]说说委托+=和-=的那些事

    写在前面 为什么会突然想说说委托?原因吗,起于一个同事的想法,昨天下班的路上一直在想这个问题,如果给委托注册多个方法,会不会都执行呢?为了一探究性,就弄了个demo研究下. += 大家都知道委托都继承 ...

  9. 【.NET基础】--委托、事件、线程(3)

    之前的两篇文章我们了解了委托和事件,本文我们看一下线程. 1,一个窗体程序,默认拥有一个线程(相当于一个商店里面,只有一个店员),这个默认的线程叫做 UI线程/主线程. 2,进程和线程的关系: A,进 ...

  10. Java基础15:深入剖析Java枚举类

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

随机推荐

  1. CentOS7 防火墙配置-详解

    CentOS 7 防火墙配置 1.防火墙的简述 防火墙对服务器起到一定的保护作用,所以了解一些相关的操作是很有必要的. 在CentOS 7.x中,有了一种新的防火墙策略叫FireWall , 在6.x ...

  2. SQL相关简单游标

    BEGIN TRY Create Table #Temp_Table_Record_Stat ( .... ) DECLARE table_Cursor CURSOR FOR SELECT objec ...

  3. Python DB

    #!/usr/bin/python #_*_ coding:utf-8 _*_ import MySQLdb import time import threading import random fr ...

  4. debian的甘特图工具

    sudo apt-get install planner

  5. 【转】Java常量池详解

    今天My partner问我一个让他头疼的Java question,求输出结果: /** * * @author DreamSea 2011-11-19 */ public class Intege ...

  6. [转][ASP.NET]ASP.NET 预编译网站

    [转自]https://msdn.microsoft.com/zh-cn/library/ms227430(v=vs.80).aspx C:\Windows\Microsoft.NET\Framewo ...

  7. 找到一篇关于2.4/5G信道的新介绍

    关于部分手机无法搜索到5G wifi信号的解决方法第一次在论坛发基础理论贴,希望能普及关于5G wifi的基础知识.         发此贴的原因是基于本人突然发现:MX3刷了3.4.1系统后,搜索不 ...

  8. Nginx+tomcat实现负载均衡的配置

    Nginx+tomcat是目前主流的java web架构,如何让nginx+tomcat同时工作呢,也可以说如何使用nginx来反向代理tomcat后端均衡呢?直接安装配置如下: 1.JAVA JDK ...

  9. 转帖:关于MongoDB你需要知道的几件事

    Henrique Lobo Weissmann 是一位来自于巴西的软件开发者,他是 itexto 公司的联合创始人,这是一家咨询公司.近日,Henrique 在博客上撰文谈到了关于 MongoDB 的 ...

  10. Java API token定时刷新

    主要用到了调度线程池: ScheduleExecutorService, 一个循环方法scheduleWithFixedDelay(方法执行完之后计算下一次开始执行时间) 使用 TokenManage ...