C#委托类型

委托(delegate)类型可以看作在代码块中植入新代码的入口标准,只要满足委托标准的代码都可以传入已存在的代码块中。本文将介绍C#中的委托类型及相关应用。

使用委托类型

委托类型为引用类型,创建时使用delegate关键字,下面的代码演示了委托的基本应用。

C#
using System;

namespace csfx_demo
{
    // 搜索文本委托类型
    delegate bool SearchDelegate(string s);
    //
    class Program
    {
        //
        static string FindFirst(string[] arr, SearchDelegate sd)
        {
            foreach(string s in arr)
            {
                if (sd(s)) return s;
            }
            return null;
        }
        //
        static void Main(string[] args)
        {
            string[] arr = { "Tom", "Jerry", "John", "Smith" };
            string result = FindFirst(arr, s => s.ToLower().Contains("s"));
            Console.WriteLine(result);  // Smith
        }
    }
}

代码中,首先定义了SearchDelegate委托类型,它包含一个string类型的参数,并返回bool类型数据。接下来,在Program类中定义了FindFirst()静态方法,其功能是查询字符串数组中第一个满足条件的元素,其中,参数一为字符串数组对象,参数二为SearchDelegate委托类型;FindFirst()方法中,当字符串元素满足SearchDelegate委托处理条件,方法会返回此字符串元素;数组中没有匹配SearchDelegate委托条件的元素时,FindFirst()方法返回null值。

Main()方法中,调用FindFirst()方法时,第二个参数使用了Lambda表达式,其格式符合SearchDelegate委托的定义,即使用一个参数,并返回bool类型数据。结果会找到并显示包含s的单词Smith。

使用委托实现运行日志的记录

很多代码在执行时有可能出现运行时错误,这些错误可以使用try语句结构进行处理,也可以进行记录并进一步分析。首先,在封装的代码库中,可以创建记录日志的委托和类,如下面的代码(/cfx/Log.cs)。

C#
namespace cfx
{
    public delegate void LogDelegate(long code, string message, string source);
    //
    public static class Log
    {
        public static LogDelegate Save =
            (long code, string message, string source) =>
            {
                // 默认什么也不做
            };
    }
}

在cfx命名空间中,首先定义了LogDelegate委托类型,包括三个参数:code指定日志类型代码,message为日志信息,source为记录来源。这里,LogDelegate委托没有返回值。

接下来定义了Log静态类,其中包含LogDelegate类型的静态字段Save,如代码中所示,默认情况下,保存日志操作什么也不做。

下面的代码,在Program.cs中测试日志记录操作。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 初始化Log.Save操作
            Log.Save = (long code, string message, string source) =>
            {
                string[] lines = { code.ToString(), message, source };
                System.IO.File.WriteAllLines(@"d:\log.txt", lines);
            };
            // 保存日志测试
            Log.Save(0, "操作成功", "Main()方法");
        }
    }
}

Main()方法中,首先重写了Log.Save字段的委托,会将三个参数的内容分行保存到d:\log.txt文本文件,执行代码后可以在此文件中查看记录的内容如下:

文本
0
操作成功
Main()方法

实际工作中,可以将代码运行日志保存到一系列的文本文件,也可以保存到数据库,并可以进一步分析。Log.Save的实现可以在程序启动时初始化,然后在项目中运行代码就可以按新的实现保存日志记录。

委托可以累加

如果委托类型没有返回值,还可以累加使用,如下面的代码。

C#
using System;

namespace csfx_demo
{
    //
    delegate void TestDelegate();
    //
    class Program
    {
        static TestDelegate Test1 = () =>
          {
              Console.WriteLine("测试委托一");
          };
        static TestDelegate Test2 = () =>
        {
            Console.WriteLine("测试委托二");
        };
        //
        static TestDelegate Test3 = () =>
        {
            Console.WriteLine("测试委托三");
        };
        //
        static void Main(string[] args)
        {
            TestDelegate test = Test1;
            test += Test2;
            test += Test3;
            test();
        }
    }
}

代码中,首先定义了TestDelegate委托,其定义没有参数和返回值;然后,定义了三个委托对象;在Main()方法中,使用+=运算符分别将Test1、Test2和Test3添加到test委托对象,执行结果如下图所示。

委托的累加使用

实现委托时,还可以直接使用方法,如下面的代码。

C#
using System;

namespace csfx_demo
{
    //
    delegate void TestDelegate();
    //
    class Program
    {
        static void Test1()
        {
            Console.WriteLine("测试委托一");
        }
        static void Test2()
        {
            Console.WriteLine("测试委托二");
        }
        //
        static void Test3() 
        {
            Console.WriteLine("测试委托三");
        }
        //
        static void Main(string[] args)
        {
            TestDelegate test = Test1;
            test += Test2;
            test += Test3;
            test();
        }
    }
}

代码执行结果与上图相同。

除了累加,还可以使用-=运算符从委托组中删除委托方法,如下面的代码。

C#
using System;

namespace csfx_demo
{
    //
    delegate void TestDelegate();
    //
    class Program
    {
        static void Test1()
        {
            Console.WriteLine("测试委托一");
        }
        static void Test2()
        {
            Console.WriteLine("测试委托二");
        }
        //
        static void Test3()
        {
            Console.WriteLine("测试委托三");
        }
        //
        static void Main(string[] args)
        {
            TestDelegate test = Test1;
            test += Test2;
            test += Test3;
            //
            test -= Test2;
            //
            test();
        }
    }
}

执行代码显示结果如下图所示。

删除委托

委托与事件

委托还有一项主要用途是实现组件或控件的事件(event),事件可以理解为当什么时间发生操作,操作的响应代码则由委托方法实现;如图形界面中的按钮控件,点击时会产生Click事件,而点击后会做什么需要通过委托方法指定。下面的代码会在自定义组件中实现事件。

C#
using System;

namespace csfx_demo
{
    // 测试委托类型
    delegate void TestDelegate();
    // 测试组件类
    class TestClass
    {
        public event TestDelegate TestEvent;
        //
        public void Run()
        {
            TestEvent();
            Console.WriteLine("TestClass.Run()方法");
        }
    }
    //
    class Program
    {
        static void Test1()
        {
            Console.WriteLine("测试事件一");
        }
        //
        static void Main(string[] args)
        {
            TestClass c1 = new TestClass();
            c1.TestEvent += Test1;
            c1.Run();
        }
    }
}

代码执行结果如下图所示。

实现事件