一、委托
-
委托的作用
-
作为函数传参:包括回调函数(如
MyHeavyJob方法执行完成后调用回调函数通知调用者)和在 LINQ 中传递规则(如Select方法对数组元素进行定制操作、PickOne方法根据规则从两个数中选一个、Where方法根据规则筛选元素)。 -
声明事件:基于委托声明事件,如按钮的点击事件。
-
-
委托的声明与使用
-
声明:在函数声明前加
delegate关键字,类似接口中声明函数(只声明不实现)。 -
实例化:实例化委托时需传递一个与委托函数签名相同的函数。
-
调用:可将委托当作函数直接调用(如
fo())或使用Invoke方法调用。 -
注册与删除方法:可以注册多个方法,注册顺序会影响调用顺序;也可以删除已注册的方法。
-
判断是否为空:可通过判断委托是否等于
null来确定其是否指向任何函数。
委托本质上是一种引用类型,可以实例化一个委托,实例化时需要传入一个与委托签名匹配的函数,之后即可调用该委托
delegate void Foo(); void MyFunc() { Console.WriteLine("hello"); } var foo = new Foo(MyFunc); // 虽然委托实例化时需要传入一个函数,但委托依然可以为空,如: // foo -= MyFunc; // 删除 MyFunc 方法,此时foo即为一个空的委托,调用时会报错 NullReferenceException // 一个委托可以注册多个方法,调用时会依次调用注册的函数 // foo += MyFunc; // 此时foo中已经注册了两次 MyFunc 方法,调用时会输出两次hello foo(); // 调用委托 // fooo.Invoke(); // 也可以使用 Invoke 方法调用带有返回值的委托在调用时只会返回最后注册的函数的返回值
-
-
经典使用案例
- 回调函数
delegate void Callback(); void MyHeavyJob(Callback callback) { Thread.Sleep(1500); callback(); } void MyCallback() { Console.WriteLine("Job done"); } MyHeavyJob(MyCallback);运行后1.5s后输出 “Job done”
- 策略模式,通过委托将“决策逻辑”(判断规则)作为参数传递
delegate bool MyRule(int x, int y); int PickOne(int a, int b, MyRule rule) { if (rule(a, b)) return a; else return b; } bool MySpecialRule1(int x, int y) { return true; } bool MySpecialRule2(int x, int y) { return false; } Console.WriteLine(PickOne(10, 20, MySpecialRule1)); Console.WriteLine(PickOne(10, 20, MySpecialRule2));运行后控制台输出 “10” 和 “20”
-
泛型委托
-
C#支持泛型委托
-
为避免每次都声明委托,可使用 .NET 提供的泛型强类型委托,如
Action(无返回值)和Func(有返回值)。 -
Action和Func有多个泛型参数版本,最多支持 16 个参数。
-
-
匿名委托与 Lambda 表达式
- 匿名委托:无需实际声明方法,直接编写匿名委托,符合相应委托要求即可。
PickOne(10, 20, delegate (int a, int b) {return true;}); - Lambda 表达式:是特殊的委托,最终会被转换为
Action或Func。在较新版本的 C# 中,编译器可自动推测 Lambda 表达式对应的强委托类型;在旧版本中,需主动写明委托类型。PickOne(10, 20, (a, b) => true);
- 匿名委托:无需实际声明方法,直接编写匿名委托,符合相应委托要求即可。
二、事件
-
事件的本质:事件是特殊的委托,为了解决委托使用时需在实例化时立即注册方法的问题。
-
事件的声明与使用
-
声明:先有委托类型,然后使用
event关键字加上委托类型和事件名称来声明事件。事件通常作为类的成员出现。delegate void MyDelegate(); // 委托类型声明 event MyDelegate MyEvent; // 实例 -
实例化与调用:在类的实例中,通过事件名称添加事件处理程序(如使用 Lambda 表达式)。
事件作为类的成员时只能在类的内部进行调用,调用时直接调用MyEvent(),或者使用?.Invoke()避免因未注册事件而报错。
-
-
事件的常见使用场景
- 当类中的某些成员(如属性、容器中的元素)发生变化时,希望外部能够观测到这些变化,可在类中声明事件,外部通过注册事件处理程序来观测。
例如
在自定义类中声明MyValueChange事件,当MyValue属性变化时触发该事件;
ObservableCollection集合有CollectionChanged事件,当对其进行增加、删除等操作时会触发该事件。
- 当类中的某些成员(如属性、容器中的元素)发生变化时,希望外部能够观测到这些变化,可在类中声明事件,外部通过注册事件处理程序来观测。
-
标准 .NET 事件模式
-
委托类型:.NET 提供了
EventHandler委托,返回值是void,传参为object sender和EventArgs e,用于处理常见的事件,如界面上的按钮点击事件、集合变化事件等。 -
事件参数类型:
EventArgs是一个基类,里面没有字段,可创建其子类来传递更多有效信息。不同的事件可能对应不同的EventArgs子类,如Button的Click事件对应RoutedEventArgs,其中包含事件相关的详细信息(如按下哪个按钮、事件是否被处理等)。 -
命名规范
▪ 事件命名:推荐使用名词 + 动词被动形式,且动词被动最好以
ed结尾,如CollectionChanged、PropertyChanged、EventRaised等;也有一些常见事件不严格遵循此规则,如Button的Click事件、KeyDown事件等。▪ 事件处理方法命名:一般在事件名称前加
On(表示事件发生的那一刻,如OnPropertyChanged)或Raise(后面可不用被动语态,如RaisePropertyChange)。
-
1345

被折叠的 条评论
为什么被折叠?



