第4章 高级特性——委托、事件、匿名方法和Lambda表达式
4.1 委托#
什么是委托?#
委托是一种知道调用方法的对象。
delegate int Dele(int x); class Test{ static int Square(int x)=>x*x; static void Main(){ Dele d=Square; int result=d(4);// 16 } }
多播委托#
所有的委托实例都拥有多播能力。即一个委托实例可以引用一组目标方法。
- 使用 += -=
delegate void Dele(int x); class Test{ static void Square(int x)=>Console.Write(x*x); static void Cube(int x)=>Console.Write(x*x*x); static void Main(){ Dele d=Square; d+=Cube; d(4);// 16 64 } }
如果委托的方法组有返回值,则委托实例返回最后一个委托方法的返回值。
泛型委托#
委托类型可以包含泛型委托类型参数。
public delegate T Dele<T>(T arg); public class Util{ public static void Transform<T>(T[] values,Dele<T> d){ for(int i=0;i<values.Length;i++){ values[i]=d(valuse[i]); } } } public class Test{ static int Square(int x)=>x*x; static void Main(){ int[] ints={1,2,3}; Util.Transform(ints,Square); foreach(int i in ints){ Console.Write(i+" ");// 1 4 9 } } }
Func和Action委托#
通用的小型委托类型,具有任意返回类型和(合理的)任意数目的参数。
对比上例:
public class Util{ public static void Transform<T>(T[] values,Func<T,T> d){ for(int i=0;i<values.Length;i++){ values[i]=d(valuse[i]); } } } public class Test{ static int Square(int x)=>x*x; static void Main(){ int[] ints={1,2,3}; Util.Transform(ints,Square); foreach(int i in ints){ Console.Write(i+" ");// 1 4 9 } } }
委托与接口#
能用委托解决的问题,都可以用接口解决。
对比上例:
public interface ITransformer{ int Transformer(int x); } public class Util{ public static void TransformAll(int[] values,ITransformer t){ for(int i=0;i<values.Length;i++){ values[i]=t.Transformer(valuse[i]); } } } public class Squarer:ITransformer{ public int Transformer(int x)=>x*x; } public class Test{ static void Main(){ int[] ints={1,2,3}; Util.TransformAll(ints,new Squarer()); foreach(int i in ints){ Console.Write(i+" ");// 1 4 9 } } }
优先使用委托的情况:
(1)接口内仅定义了一个方法
(2)需要多播能力
(3)订阅者需要多次实现接口
委托的兼容性#
即使签名相似,委托类型也互不兼容。
delegate void D1(); delegate void D2(); D1 d1=Method; D2 d2=d1 // 编译错误 D2 d2=new D2(d1);
如果委托实例指向同一个方法,则认为它们是等价的。
delegate void D(); D d1=Method(); D d2=Method(); Console.WriteLine(d1==d2); // true
委托可以比指向的目标方法的参数更具体化,这称为逆变。
delegate void StringAction(string s); class Test{ static void Method(object o)=>Console.WriteLine(o); static void Main(){ StringAction sa=new StringAction(Method); sa("hello"); // hello } } // 这里object->string->object,后部分向上转型
指向的目标方法的参数可以比委托更具体化,这称为协变。
delegate object StringAction(); class Test{ static string Method()=>Console.WriteLine("hello"); static void Main(){ StringAction sa=new StringAction(Method); object o=sa; Console.WriteLine(o); // hello } } // 这里string->object->string,后部分向下转型
自定义泛型委托,最好使用下列标准(为了类型的关系自然的转化):
(1)将只用于返回值类型的类型参数标记为协变(out)
(2)将只用于参数的任意类型参数标记为逆变(in)
delegate TResult Func<out TResult>(); Func<string> x=...; Func<object> y=x;// string->object delegate void Action<in T>(T arg); Func<object> x=...; Func<string> y=x;// object->string
4.2 事件#
什么是事件?#
public delegate void TestHandler(); public class Test{ public event TestHandler TestChanged; }
事件修饰符:virtual、override、abstract、sealed
事件有什么用?#
上例中,TestChanged在Test类内当作一个委托使用。
而Test类外面的TestHandler委托,只能在TestChanged事件身上进行+=、-=操作。
这样做的目的就是为了保证订阅者之间不互相影响。提高健壮性
4.3 匿名方法#
什么是匿名方法?#
没有方法名的方法,只能用一次,必须是一个语句块 { }
delegate int Dele(int i); Dele d=delegate (int x) {return x*x;}; Console.WriteLine(d(4)); // 16
4.4 Lambda表达式#
什么是Lambda表达式?#
Lambda表达式是一种可以替代委托实例的匿名方法。
对比上例:
delegate int Dele(int i); Dele d1= (int x)=> {return x*x}; Console.WriteLine(d1(4)); // 16 Dele d2= x=> x*x; Console.WriteLine(d2(4)); // 16
形式:
(参数) => 表达式或者语句块
- 为了方便,只有一个可推测类型的参数时,可以省略小括号。
Lambda表达式通常和Func和Action委托一起使用。对比上例:
Func<int,int> d= x=>x*x;
显示指定Lambda参数的类型#
编译器可以根据上下文推断出Lambda表达式的类型,但是当无法判断时就必须显示指定每一个参数的类型。
void Foo<T>(T x){} void Bar<T>(Action<T> x){} //Bar(x=>Foo(x)); // ??无法判断 Bar((int x)=>Foo(x)); Bar<int>( x=>Foo(x) ); Bar<int>(Foo);
捕获外部变量#
Lambda表达式可以引用方法内定义的局部变量和方法的参数。
- 这些变量和外部参数称为捕获变量,捕获变量的表达式称为闭包
捕获变量在真正调用委托时赋值,而不是在捕获时赋值。
Lambda表达式可以更新捕获的变量的值。
捕获变量的生命周期和委托的生命周期一样,委托执行完毕后,捕获变量会从作用域消失,变量值会变回去。
Lambda表达式和局部方法的对比#
局部方法和Lambda表达式的相应功能是重叠的。
局部方法的优势:
(1)局部方法可直接调用自己(递归)
(2)局部方法避免了定义杂乱的委托类型
(3)局部方法的开销更小
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!