首页 > 代码库 > 委托通常可以应用到哪些场合
委托通常可以应用到哪些场合
分析问题
委托的功能和其名字非常类似,在设计中其思想在于把工作委派给其他特定的类型或者组件。委托的使用者可以理解为工作的分派者,在通常情况下使用者清楚地知道哪些工作需要执行、执行的结果又是什么,但是他却不会亲自地去做这些工作,而是恰当地把这些工作分派出去。
在本节中,笔者将举一个日志读写的例子,来说明委托的实际应用。日志子系统在几乎所有的系统中都有所应用,在一般情况下,日志子系统的使用者希望的都是一个单一的方法,传入日志内容和日志类型,而日志系统会根据具体情况来进行写日志动作。对于日志系统的设计者来说,写一条日志可能需要包含一系列的工作,而日志系统决定把这些工作进行恰当的分派,这时就需要使用一个委托成员。下面代码展示了这个日志系统的简单实现方式。
using System;using System.IO;using System.Text;namespace Test{ /// <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); public sealed partial class LogManager:IDisposable { //书写日志的组件 private Type _componentType; //日志文件 private string _logFile; //日志文件读写流 private FileStream _fs; //用来写日志的委托 public Log writeLog; //锁 private static object mutext = new object(); public LogManager() { writeLog = new Log(PreppareLogFile); 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(); } } #region 和日志文件有关的操作 /// <summary> /// 如果日志文件不存在,则新建日志文件 /// </summary> private void PreppareLogFile(string content, LogType type) { //只允许单线程创建日志文件 lock (mutext) { if (File.Exists(_logFile)==false) { 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(); } #endregion #region 和时间记录有关的操作 /// <summary> /// 为日志添加当前UTC时间 /// </summary> private void AppendUTCTime(string content, LogType type) { //得到当前UTC时间 string time = DateTime.Now.ToUniversalTime().ToString(); byte[] con = Encoding.Default.GetBytes(time); _fs.Write(con, 0, 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, 0, con.Length); } #endregion #region 和日志内容有关的操作 /// <summary> /// 添加日志内容 /// </summary> private void AppendContent(string content, LogType type) { byte[] con = Encoding.Default.GetBytes(content); _fs.Write(con, 0, con.Length); } /// <summary> /// 为日志添加组件类型 /// </summary> private void AppendComponentType(string content, LogType type) { byte[] con = Encoding.Default.GetBytes(_componentType.ToString()); _fs.Write(con, 0, 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.Trace: typeString = "Trace"; break; case LogType.Info: typeString = "Info"; break; case LogType.Warn: typeString = "Warn"; break; case LogType.Error: typeString = "Error"; break; default: typeString = ""; break; } byte[] con = Encoding.Default.GetBytes(typeString); _fs.Write(con, 0, con.Length); } #endregion #region 和日志格式控制有关的操作 /// <summary> /// 添加分隔符 /// </summary> private void AppendSeperator(string content, LogType type) { byte[] con = Encoding.Default.GetBytes(" | "); _fs.Write(con, 0, con.Length); } /// <summary> /// 添加换行符 /// </summary> private void AppendNewLine(string content, LogType type) { byte[] con = Encoding.Default.GetBytes("\r\n"); _fs.Write(con, 0, con.Length); } #endregion #region 修改所使用的时间类型 /// <summary> /// 设置使用UTC时间 /// </summary> public void UseUTCTime() { writeLog = new Log(PreppareLogFile); writeLog += OpenStream; //这里添加记录UTC时间的方法 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(PreppareLogFile); writeLog += OpenStream; //这里添加记录本地时间的方法 writeLog += AppendLocalTime; writeLog += AppendSeperator; writeLog += AppendComponentType; writeLog += AppendSeperator; writeLog += AppendType; writeLog += AppendSeperator; writeLog += AppendContent; writeLog += AppendNewLine; writeLog += CloseStream; } #endregion } class UseLog { /// <summary> /// 使用日志管理类型来记录日志 /// </summary> static void Main() { //使用日志 using (LogManager logManager = new LogManager(Type.GetType("Test.UseLog"), @"D:\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); } } }}
在上述代码所示的Log子系统中,使用了一个Log类型的委托来做所有的日志工作,而LogManager只需要管理这个委托,负责分派任务即可。代码中初始化委托成员的过程即是任务分派的过程,读者可能已经注意到,LogManager的UseUTCTime和UseLocalTime方法都对委托成员进行了重新分配,这可以理解为任务的再分配。
说明
委托作为日志子系统的另一优点就在于可以灵活地分派任务,并且随时可以重新整理。读者可以尝试为LogManager添加其他功能,例如尝试提供其他编码的日志,并且提供重分配的公共方法来让使用者进行选择。
下面是以上代码执行后的日志输出:
答案
委托的应用场合通常是任务的执行者把细节工作进行再分配,执行者确切地知道什么工作将要被执行,但却把执行细节委托给其他组件、方法或者程序集。
委托通常可以应用到哪些场合