委托专题篇
什么是委托?(what)
写了一个委托的例子如下:
public delegate void DelegateTest();
我们通过ILSPY反编译工具将这段C#代码通过反编译工具找到其编译后的IL代码可以看到如下:
从上图看出, 实际上可以将委托理解成为一个类.
这个类中有三个方法, 通过反编译委托类中可以看到, 分别是Invoke(执行委托的方法)
, BeginInvoke(异步开始执行委托的方法)
, EndInvoke(异步结束委托的方法)
.
委托既然是一种类, 那么它就应该符合类的所有特征了.比如能够实例化, 委托类实例化出来的实例就叫做委托实例. 通过委托实例来调用上面所提到的三个方法来执行实例化时包裹的方法.比如这样实例化:
DelegateTest delegateTest = new DelegateTest(DoNothing);
public void DoNothing()
{
Console.WriteLine("this is a delegate test.");
}
delegateTest.Invoke();
可以看到我们实例化的过程中将一个方法传递给了委托类, 实例化后通过Invoke()去调用.这就是委托.
为什么用委托?(why)
委托能够将不同方法的重复逻辑进行解耦,和代码复用.
我们写code的时候会发现很多情况我们需要对某段代码增添异常处理, log输出处理,这些common的逻辑实际上可以通过委托巧妙地给抽离出来, 比如这段代码:
/// <summary> /// 对于日志类的异常封装. /// </summary> /// <param name="action"></param> public static void SafeInvoke(this Action action) { try { action.Invoke(); } catch (Exception e) { Console.WriteLine(e.ToString()); } }
或者对于数据库的访问总是千篇一律, 使用委托可以抽离这些逻辑:
/// <summary> /// 封装的ADO.net 的数据库访问类. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sql"></param> /// <param name="func"></param> /// <returns></returns> public static T Excute<T>(string sql, Func<SqlCommand, T> func) { using (SqlConnection connection = new SqlConnection("")) { connection.Open(); SqlTransaction transaction = connection.BeginTransaction(); T t = default(T); new Action(() => { SqlCommand command = new SqlCommand(); t = func(command); transaction.Commit(); }).SafeInvoke(); return t; } }
能够使用共享变量
这个一般是针对匿名函数来说的, 因为匿名函数相当于函数中的函数, 可以使用上层函数内的一些变量. 如果不用匿名函数的话, 需要新写一个方法, 变量则需要通过方法的参数进行传递. 所以在编写代码的时候可以灵活使用匿名函数或者lambda表达式.lambda表达式使代码更简洁
这个就不解释了. 后面会单独介绍lambda表达式以及它的发展史.
怎么用委托?(how)
由于委托的使用场景有很多, 我们平时接触到的基本上都涉及到委托的应用.比较普遍的就是lambda表达式,匿名函数等等. 我写了几个demo,都是平时写委托的时候不太熟练的情况下去查看一下就行了.
委托的高阶用法
这里主要是用文字介绍一下委托的一些高阶用法, 并且也是通用用法了. 委托发展到现在, 基本上大家都在使用的都是高阶用法, 过去的用法已经在委托随着 .net framework和C#的逐渐发展已经不怎么用了. 所以主要是需要了解一下目前正使用的, 当然也要稍微了解一下是如何发展来的.
我们以 .net的版本为时间线梳理委托的演变
.net版本 | 描述 | 代码示例 |
---|---|---|
.net framework 1.0 | 委托声明,委托实例化, 实例化需要真实的传递与委托声明匹配的参数的方法 | DelegateTest delegateTest = new DelegateTest(DoNothing); |
.net framework 2.0 | 委托可以传递匿名方法, 无需实际构造一个方法 | StringProcesser tomVoice = new StringProcesser(delegate (string s1){Console.WriteLine(s1);});tomVoice.Invoke("Hello Daddy."); |
.net framework 3.0 | 引入lambda表达式, 其实就是为了简化匿名函数的写法 | StringProcesser background = (s2) => Console.WriteLine(s2); |
.net framework 4.0 | 引入泛型委托 Action<>, Func<out T result> 的用法, 因为之前程序开发人员在构造委托类的时候, 会构造很多个委托类, 它们彼此之间毫无关联,这不是一个面向抽象的好的解决方案. 泛型委托的出现后, 基本上后续的所有关于委托的实现都使用这两个了. 所以它们很重要 . |
Action action = new Action(() => Console.WriteLine("this is action")); Func<int, string> func = new Func<int, string>((i) => i.ToString()); |
Action 和 Func
首先看两个图
- 可以看到它们都被声明为委托. 区别就是Action是没有返回值的, 而Func要求有一个泛型的返回值. 所以这个区别决定了在写code的时候使用哪种泛型委托.
- 二者的参数列表可以很多, .net内部提供了多达16个泛型参数. 不够你自己也可以加. Func在这里有一点区别就是在最后的参数是作为返回的结果的.
它们的使用方式与正常的委托使用方式一样的.举几个例子
Action action = new Action(() => Console.WriteLine("this is action")); Func<int, string> func = new Func<int, string>((i) => i.ToString());