.NET基础 (15)委托

委托
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(0);
            d(1);
            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源码

 1         // This method will combine this delegate with the passed delegate
 2         //    to form a new delegate.
 3         [System.Security.SecuritySafeCritical]  // auto-generated
 4         protected override sealed Delegate CombineImpl(Delegate follow)
 5         {
 6             if ((Object)follow == null) // cast to object for a more efficient test
 7                 return this;
 8
 9             // Verify that the types are the same...
10             if (!InternalEqualTypes(this, follow))
11                 throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
12
13             MulticastDelegate dFollow = (MulticastDelegate)follow;
14             Object[] resultList;
15             int followCount = 1;
16             Object[] followList = dFollow._invocationList as Object[];
17             if (followList != null)
18                 followCount = (int)dFollow._invocationCount;
19
20             int resultCount;
21             Object[] invocationList = _invocationList as Object[];
22             if (invocationList == null)
23             {
24                 resultCount = 1 + followCount;
25                 resultList = new Object[resultCount];
26                 resultList[0] = this;
27                 if (followList == null)
28                 {
29                     resultList[1] = dFollow;
30                 }
31                 else
32                 {
33                     for (int i = 0; i < followCount; i++)
34                         resultList[1 + i] = followList[i];
35                 }
36                 return NewMulticastDelegate(resultList, resultCount);
37             }
38             else
39             {
40                 int invocationCount = (int)_invocationCount;
41                 resultCount = invocationCount + followCount;
42                 resultList = null;
43                 if (resultCount <= invocationList.Length)
44                 {
45                     resultList = invocationList;
46                     if (followList == null)
47                     {
48                         if (!TrySetSlot(resultList, invocationCount, dFollow))
49                             resultList = null;
50                     }
51                     else
52                     {
53                         for (int i = 0; i < followCount; i++)
54                         {
55                             if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
56                             {
57                                 resultList = null;
58                                 break;
59                             }
60                         }
61                     }
62                 }
63
64                 if (resultList == null)
65                 {
66                     int allocCount = invocationList.Length;
67                     while (allocCount < resultCount)
68                         allocCount *= 2;
69
70                     resultList = new Object[allocCount];
71
72                     for (int i = 0; i < invocationCount; i++)
73                         resultList[i] = invocationList[i];
74
75                     if (followList == null)
76                     {
77                         resultList[invocationCount] = dFollow;
78                     }
79                     else
80                     {
81                         for (int i = 0; i < followCount; i++)
82                             resultList[invocationCount + i] = followList[i];
83                     }
84                 }
85                 return NewMulticastDelegate(resultList, resultCount, true);
86             }
87         }

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

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);

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

 1     /// <summary>
 2     /// 日志管理类型,基本成员和构造方法
 3     /// </summary>
 4     public sealed partial class LogManager:IDisposable
 5     {
 6         private Type _componentType;
 7         private String _logfile;
 8         private FileStream _fs;
 9         public Log WriteLog;         //用来写日志的委托
10         //锁
11         private static object mutext = new object();
12         //严格控制无参的构造方法
13         private LogManager()
14         {
15             WriteLog = new Log(PrepareLogFile);
16             WriteLog += OpenStream; //打开流
17             WriteLog += AppendLocalTime;    //添加本地时间
18             WriteLog += AppendSeperator;    //添加分隔符
19             WriteLog += AppendComponentType;//添加模块类别
20             WriteLog += AppendSeperator;    //添加分隔符
21             WriteLog += AppendType;         //添加日志类别
22             WriteLog += AppendSeperator;    //添加分隔符
23             WriteLog += AppendContent;      //添加内容
24             WriteLog += AppendNewLine;      //添加回车
25             WriteLog += CloseStream;        //关闭流
26         }
27         /// <summary>
28         /// 构造方法
29         /// </summary>
30         /// <param name="type">使用该日志的类型</param>
31         /// <param name="file">日志文件全路径</param>
32         public LogManager(Type type, String file):this()
33         {
34             _logfile = file;
35             _componentType = type;
36
37         }
38         /// <summary>
39         /// 释放FileStream对象
40         /// </summary>
41         public void Dispose()
42         {
43             if (_fs != null)
44                 _fs.Dispose();
45             GC.SuppressFinalize(this);
46         }
47         ~LogManager()
48         {
49             if (_fs != null)
50                 _fs.Dispose();
51         }
52
53     }

委托链上的方法:

  1     /// <summary>
  2     /// 委托链上的方法(和日志文件有关的操作)
  3     /// </summary>
  4     public sealed partial class LogManager:IDisposable
  5     {
  6         /// <summary>
  7         /// 如果日志文件不存在,则新建日志文件
  8         /// </summary>
  9         private void PrepareLogFile(String content, LogType type)
 10         {
 11             //只允许单线程创建日志文件
 12             lock(mutext)
 13             {
 14                 if (!File.Exists(_logfile))
 15                     using (FileStream fs = File.Create(_logfile))
 16                     { }
 17             }
 18         }
 19         /// <summary>
 20         /// 打开文件流
 21         /// </summary>
 22         private void OpenStream(String content, LogType type)
 23         {
 24             _fs = File.Open(_logfile, FileMode.Append);
 25         }
 26         /// <summary>
 27         /// 关闭文件流
 28         /// </summary>
 29         private void CloseStream(String content, LogType type)
 30         {
 31             _fs.Close();
 32             _fs.Dispose();
 33         }
 34     }
 35     /// <summary>
 36     /// 委托链上的方法(和日志时间有关的操作)
 37     /// </summary>
 38     public sealed partial class LogManager : IDisposable
 39     {
 40         /// <summary>
 41         /// 为日志添加当前UTC时间
 42         /// </summary>
 43         private void AppendUTCTime(String content, LogType type)
 44         {
 45             String time=DateTime.Now.ToUniversalTime().ToString();
 46             Byte[] con = Encoding.Default.GetBytes(time);
 47             _fs.Write(con, 0, con.Length);
 48         }
 49         /// <summary>
 50         /// 为日志添加本地时间
 51         /// </summary>
 52         private void AppendLocalTime(String content, LogType type)
 53         {
 54             String time = DateTime.Now.ToLocalTime().ToString();
 55             Byte[] con = Encoding.Default.GetBytes(time);
 56             _fs.Write(con, 0, con.Length);
 57         }
 58     }
 59     /// <summary>
 60     /// 委托链上的方法(和日志内容有关的操作)
 61     /// </summary>
 62     public sealed partial class LogManager : IDisposable
 63     {
 64         /// <summary>
 65         /// 添加日志内容
 66         /// </summary>
 67         private void AppendContent(String content, LogType type)
 68         {
 69             Byte[] con = Encoding.Default.GetBytes(content);
 70             _fs.Write(con, 0, con.Length);
 71         }
 72         /// <summary>
 73         /// 为日志添加组件类型
 74         /// </summary>
 75         private void AppendComponentType(String content, LogType type)
 76         {
 77             Byte[] con = Encoding.Default.GetBytes(type.ToString());
 78             _fs.Write(con, 0, con.Length);
 79         }
 80         /// <summary>
 81         /// 添加日志类型
 82         /// </summary>
 83         private void AppendType(String content, LogType type)
 84         {
 85             String typestring = String.Empty;
 86             switch (type)
 87             {
 88                 case LogType.Debug:
 89                     typestring = "Debug";
 90                     break;
 91                 case LogType.Error:
 92                     typestring = "Error";
 93                     break;
 94                 case LogType.Info:
 95                     typestring = "Info";
 96                     break;
 97                 case LogType.Trace:
 98                     typestring = "Trace";
 99                     break;
100                 case LogType.Warn:
101                     typestring = "Warn";
102                     break;
103                 default:
104                     typestring = "";
105                     break;
106             }
107             Byte[] con = Encoding.Default.GetBytes(typestring);
108             _fs.Write(con, 0, con.Length);
109         }
110     }
111     /// <summary>
112     /// 委托链上的方法(和日志的格式控制有关的操作)
113     /// </summary>
114     public sealed partial class LogManager : IDisposable
115     {
116
117         /// <summary>
118         /// 添加分隔符
119         /// </summary>
120         private void AppendSeperator(String content, LogType type)
121         {
122             Byte[] con = Encoding.Default.GetBytes(" | ");
123             _fs.Write(con, 0, con.Length);
124         }
125         /// <summary>
126         /// 添加换行符
127         /// </summary>
128         private void AppendNewLine(String content, LogType type)
129         {
130             Byte[] con = Encoding.Default.GetBytes("\r\n");
131             _fs.Write(con, 0, con.Length);
132         }
133     }
134     /// <summary>
135     /// 修改所使用的时间类型
136     /// </summary>
137     public sealed partial class LogManager : IDisposable
138     {
139         /// <summary>
140         /// 设置使用UTC时间
141         /// </summary>
142         public void UseUTCTime()
143         {
144             WriteLog = new Log(PrepareLogFile);
145             WriteLog += OpenStream;
146             WriteLog += AppendUTCTime;
147             WriteLog += AppendSeperator;
148             WriteLog += AppendComponentType;
149             WriteLog += AppendSeperator;
150             WriteLog += AppendType;
151             WriteLog += AppendSeperator;
152             WriteLog += AppendContent;
153             WriteLog += AppendNewLine;
154             WriteLog += CloseStream;
155         }
156         /// <summary>
157         /// 设置使用本地时间
158         /// </summary>
159         public void UseLocalTime()
160         {
161             WriteLog = new Log(PrepareLogFile);
162             WriteLog += OpenStream;
163             WriteLog += AppendLocalTime;
164             WriteLog += AppendSeperator;
165             WriteLog += AppendComponentType;
166             WriteLog += AppendSeperator;
167             WriteLog += AppendType;
168             WriteLog += AppendSeperator;
169             WriteLog += AppendContent;
170             WriteLog += AppendNewLine;
171             WriteLog += CloseStream;
172         }
173     }

调用:

    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

时间: 09-09

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

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

[.net 面向对象编程基础] (20)  委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1.什么是委托? 学习委托,我想说,学会了就感觉简单的不能再简单了,没学过或都不愿了解的人,看着就头大,其实很简单.委托在.net面向对象编程和学习设计模式中非常重要,是学习.net面向对象编程必须要学会并掌握的. 委托从字面上理解,就是把做一些事情交给别人来帮忙完成.在C#中也可以这样理解,委托就是动态调用方法.这样说明,就很好理解了. 平时我们会

C# 1的核心基础之一——委托

C# 1的核心基础之一--委托 C# 1的核心基础之一--委托简单委托的构成合并和删除委托事件的简单讨论委托总结 简单委托的构成 声明委托类型: delegate void StringProcessor(string input); 为委托实例的操作找到一个恰当的方法 void PrintString(string x)完全符合要求 void PrintInteger(int x)参数类型不兼容 void PrintTwoStrings(string x, string y)参数个数不匹配 i

OC基础(15)

@property参数 @Property练习 @class 循环retian *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30px; margin-left: -3

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

[.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义为一个虚方法或抽象方法.这节单独把抽象类提出来,是因为抽象是.net实现面向对象编程重要的重要思想,定义抽象类就象一个模板一个提纲,它可以理解为中央的指导思想,我们通过派生类去具体实现它.由此可见,抽象类本身没有任何作用,也不能被实例化,因为他本身就不具有具体的事物.比如上节的动物类的例 子,我们实

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

Java基础15:深入剖析Java枚举类 枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示. 初探枚举类 在程序设计中,有时会用到由若干个有限数据元素组成的集合,如一周内的星期一到星期日七个数据元素组成的集合,由三种颜色红.黄.绿组成的集合,一个工作班组内十个职工组成的集合等等,程序中某个变量取值仅限于集合中的元素.此时,可将这些数据集合定义为枚举类型. 因此,枚举类型是某类数据可能取值的集合,如一周内星期可能取值的

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

写在前面 为什么会突然想说说委托?原因吗,起于一个同事的想法,昨天下班的路上一直在想这个问题,如果给委托注册多个方法,会不会都执行呢?为了一探究性,就弄了个demo研究下. += 大家都知道委托都继承自System.MulticastDelegate,而System.MulticastDelegate又继承自System.Delegate,可以通过+=为委托注册多个方法.那么他们是否都执行了呢?执行的结果又是怎样的呢?有返回值和没返回值的是否结果是否一样?那就试着说说+=都干了哪些事? 测试代码

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

之前的两篇文章我们了解了委托和事件,本文我们看一下线程. 1,一个窗体程序,默认拥有一个线程(相当于一个商店里面,只有一个店员),这个默认的线程叫做 UI线程/主线程. 2,进程和线程的关系: A,进程,包含程序运行所需要的资源 ,在大多数情况下是指 程序.(商店:囤积要使用的资源的地方) B,线程,是在进程中能够被CPU调用的程序单元,是提供给CPU运行程序的代码片段.(商店员工:是运行程序的行动者) C,一个进程至少一个线程,每一个线程有自己专属的寄存器(栈指针.程序计数器等)但代码区是共享

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

前言:上一篇介绍了下多线程的相关知识:C#基础系列--多线程的常见用法详解,里面就提到了委托变量.这篇简单介绍下委托的使用.当然啦,园子里面很多介绍委托的文章都会说道:委托和事件的概念就像一道坎,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里发慌.确实这东西就像最开始学C语言的指针一样,令人有一种很纠结的感觉,总觉得要调用一个方法直接调用就行了,为啥非要定义一个委托时执行这个方法呢.其实在C#里面很多的技术都是为了重用和简化代码而生,委托也不例外,很多使用C#多态去

4.net基础之委托事件

委托的声明public delegate void NoReturnNoPara();访问修饰符 delegate关键字 返回值 委托名委托的调用NoReturnNoPara nrnp = new NoReturnNoPara(传递方法名);传递的方法必须满足,没有返回值,没有参数的限制.实例方法,静态方法,虚方法nrnp.Invoke(); 泛型委托Func:接收0-17个输入参数,一个返回参数的泛型委托Action:接收0-17个输入参数,0个返回参数的泛型委托 委托的意义:解耦使用泛型+委

JavaScript基础15——js的DOM对象

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>js的DOM对象</title> 6 <script type="text/javascript"> 7 // DOM:Document Object Model 文档对象模型 8 /* 9 文档:超文本文档html.xml 10 对象:提供了属