委托(delegate)类型可以看作在代码块中植入新代码的入口标准,只要满足委托标准的代码都可以传入已存在的代码块中。本文将介绍C#中的委托类型及相关应用。
委托类型为引用类型,创建时使用delegate关键字,下面的代码演示了委托的基本应用。
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)。
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中测试日志记录操作。
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的实现可以在程序启动时初始化,然后在项目中运行代码就可以按新的实现保存日志记录。
如果委托类型没有返回值,还可以累加使用,如下面的代码。
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委托对象,执行结果如下图所示。
实现委托时,还可以直接使用方法,如下面的代码。
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(); } } }
代码执行结果与上图相同。
除了累加,还可以使用-=运算符从委托组中删除委托方法,如下面的代码。
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事件,而点击后会做什么需要通过委托方法指定。下面的代码会在自定义组件中实现事件。
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(); } } }
代码执行结果如下图所示。