C#委托都有哪些可以应用的场合?

委托的功能和其名字非常类似,在设计中其思想在于将工作委派给其他特定的类型、组件、方法或程序集。委托的使用者可以理解为工作的分派者,在通常情况下使用者清楚地知道哪些工作需要执行、执行的结果又是什么,但是他不会亲自地去做这些工作,而是恰当地把这些工作分派出去.

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

下面的代码展示了该日志子系统的简单实现方式:

① 定义枚举:日志的类别

public enum LogType
{
    Debug,
    Trace,
    Info,
    Warn,
    Error
}

② 定义委托,由日志使用者直接执行来完成写日志的工作

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();
    //严格控制无参的构造方法
    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, 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);
    }
}

/// <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, 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.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, 0, 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, 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);
    }
}

/// <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;
    }
}

日志管理类定义了一些列符合Log委托的方法,这些方法可以被添加到记录日志的委托对象之中,以构成整个日志记录的动作。在日后的扩展中,主要的工作也集中在添加新的符合Log委托定义的方法,并且将其添加到委托链上。

④ 在Main方法中调用LogManager的Log委托实例来写日志,LogManager只需要管理这个委托,负责分派任务即可。

public class Program
{
    public static void Main(string[] args)
    {
        //使用日志
        using (LogManager logmanager =
            new LogManager(Type.GetType("LogSystem.Program"), "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);
        }

        Console.ReadKey();
    }
}

代码中初始化委托成员的过程既是任务分派的过程,可以注意到LogManager的UseUTCTime和UseLocalTime方法都是被委托成员进行了重新的分配,也可以理解为任务的再分配。

下图是上述代码的执行结果,将日志信息写入了D:\TestLog.txt中:

C#委托都有哪些可以应用的场合?