委托
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. settimeout()在IE8下参数无效问题解决方法

    遇到这个问题,setTimeout(Scroll(),3000); 这种写法在IE8 下 不能够执行,提示参数无效, setTimeout(function(){Scroll()},3000);这种方 ...

  2. 【python】smtp邮件发送

    纯文本: #!/usr/bin/env python3 #coding: utf-8 import smtplib from email.mime.text import MIMEText from ...

  3. java 方法(函数)

    所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 一般情况下,定义一个方法的语法是: 其中: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 public.protected.p ...

  4. ThinkJava-新IO

    package com.java.io; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io ...

  5. 机房用ROS创建时间服务器

    发现机房里的服务器时间老是不同步,虽然都设置为time-a.nist.gov和time-b.nist.gov,仍然有失败的概率.可能是因为国外服务器的缘故.所以打算在机房里创建一个时间服务器.正好RO ...

  6. Educational Codeforces Round 37-F.SUM and REPLACE题解

    一.题目 二.题目链接 http://codeforces.com/contest/920/problem/F 三.题意 给定$N$个范围在$[1, 1e6)$的数字和$M$个操作.操作有两种类型: ...

  7. phpcms模块开发中的小问题及解决方法

    1.模块菜单中文名出错 在编写安装模块时候可能需要更改extention.inc.php中定义中文名称,由于反复安装或者通过phpcms的扩展->菜单管理 修改菜单名会导致中文名失败.解决办法很 ...

  8. application/json 和 application/x-www-form-urlencoded的区别

    public static string HttpPost(string url, string body) { //ServicePointManager.ServerCertificateVali ...

  9. PyDev for eclipse 插件下载地址

    PyDev for eclipse 插件下载地址http://sourceforge.net/projects/pydev/files/pydev/python解释器以及python类库下载地址htt ...

  10. Basic64 编码解码

    import sun.misc.BASE64Decoder; public class Base64 { /** * 字符串转Base64编码 * @param s * @return */ publ ...