VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • 委托与事件基础——用C#开发按钮点击事件模拟

第一部分:C#基础入门
实例7:委托与事件基础——按钮点击事件模拟
实例介绍
在UI开发中,按钮点击是最常见的交互场景:用户点击按钮→按钮“通知”所有关心这个事件的对象(比如窗体、日志系统)→对象执行相应操作。这个“通知机制”的核心就是委托和事件:
委托:是方法的“容器”,可以存储多个方法的引用,相当于“方法指针的集合”;
事件:是委托的“安全封装”,限制外部只能订阅/取消订阅事件,不能vb.net教程C#教程python教程SQL教程access 2010教程直接触发或修改委托,避免滥用。
本实例通过模拟Windows按钮的点击事件,带你掌握委托的定义、事件的声明、订阅/触发事件的全流程,理解.NET事件模型的核心规范(如sender+EventArgs),学会用事件驱动思维编写松耦合的代码。
需求分析
1.定义委托类型:ButtonClickHandler,遵循.NET事件规范(参数为sender+EventArgs);
2.模拟按钮类:Button,包含Click事件,以及触发事件的方法OnClick;
3.模拟订阅者:
Form类:模拟窗体,处理按钮点击时显示消息;
LogManager类:模拟日志系统,记录按钮点击时间;
4.订阅事件:窗体和日志系统订阅按钮的Click事件;
5.触发事件:模拟用户点击按钮,触发Click事件,通知所有订阅者。
代码实现
csharp

	using System;
	
	namespace EventDemo
	{
	// 1. 定义委托类型(遵循.NET事件规范:sender+EventArgs)
	/// <summary>
	/// 按钮点击事件的委托类型
	/// </summary>
	/// <param name="sender">触发事件的对象(如按钮)</param>
	/// <param name="e">事件数据(无额外数据时用EventArgs.Empty)</param>
	public delegate void ButtonClickHandler(object sender, EventArgs e);
	
	// 2. 模拟按钮类(事件的触发者)
	public class Button
	{
	// 2.1 声明事件(用event关键字封装委托)
	public event ButtonClickHandler Click;
	
	// 2.2 触发事件的方法(protected virtual:允许子类重写,内部触发)
	protected virtual void OnClick(EventArgs e)
	{
	// 空合并运算符:如果Click有订阅者,则调用Invoke触发事件
	Click?.Invoke(this, e);
	}
	
	// 2.3 模拟用户点击按钮(对外公开的方法)
	public void SimulateClick()
	{
	Console.WriteLine("
=== 模拟用户点击按钮 ===");
	// 触发事件:传递sender(this即按钮本身)和空事件数据
	OnClick(EventArgs.Empty);
	}
	}
	
	// 3. 模拟窗体类(事件的订阅者1:处理点击显示消息)
	public class Form
	{
	public string FormName { get; set; }
	
	public Form(string name)
	{
	FormName = name;
	}
	
	// 3.1 事件处理方法(必须匹配委托的签名)
	public void Btn_Click(object sender, EventArgs e)
	{
	// sender是触发事件的按钮,可强制转换获取按钮信息
	Button btn = sender as Button;
	Console.WriteLine($"[{FormName}]收到按钮点击事件!触发者:{btn.GetType().Name}");
	}
	}
	
	// 4. 模拟日志类(事件的订阅者2:记录点击时间)
	public static class LogManager
	{
	// 4.1 静态事件处理方法(无需实例化)
	public static void LogClickEvent(object sender, EventArgs e)
	{
	DateTime now = DateTime.Now;
	Console.WriteLine($"[日志系统][{now:yyyy-MM-dd HH:mm:ss}]按钮被点击");
	}
	}
	
	// 主程序:测试事件流程
	class Program
	{
	static void Main(string[] args)
	{
	// 创建按钮和窗体
	Button loginBtn = new Button();
	Form mainForm = new Form("主窗体");
	
	// 订阅事件:窗体和日志系统都订阅按钮的Click事件
	loginBtn.Click += mainForm.Btn_Click; // 实例方法订阅
	loginBtn.Click += LogManager.LogClickEvent; // 静态方法订阅
	
	// 模拟点击按钮:触发事件,通知所有订阅者
	loginBtn.SimulateClick();
	
	// 取消订阅:日志系统不再接收事件
	loginBtn.Click -= LogManager.LogClickEvent;
	Console.WriteLine("
=== 取消日志订阅后,再次点击 ===");
	loginBtn.SimulateClick();
	}
	}
	}

逐行讲解

  1. 委托定义(ButtonClickHandler)
    csharp
    public delegate void ButtonClickHandler(object sender, EventArgs e);
    委托语法:delegate关键字+返回值+委托名+参数列表;
    .NET事件规范:参数必须是sender(触发事件的对象,类型为object)和e(事件数据,类型为EventArgs或其子类),这样订阅者能知道“谁触发了事件”和“事件发生时的额外信息”;
    作用:定义事件的“签名”,所有订阅者的处理方法必须匹配这个签名。
  2. 按钮类(Button)中的事件
    csharp
    public event ButtonClickHandler Click;
    event关键字:是委托的“安全包装”,限制外部代码的操作:
    外部只能用+=(订阅)和-=(取消订阅)事件;
    外部不能直接赋值(如loginBtn.Click = ...)或触发事件(如loginBtn.Click());
    作用:保护委托不被滥用,确保事件只能由触发者(按钮)内部触发。
  3. 触发事件的方法(OnClick)
    csharp
	protected virtual void OnClick(EventArgs e)
	{
	Click?.Invoke(this, e);
	}

protected:只能在类内部或子类中调用,防止外部触发;
virtual:允许子类重写(比如自定义按钮可以修改触发逻辑);
Click?.Invoke(this,e):
?.是空合并运算符:如果Click委托有订阅者(即不为null),则调用Invoke方法触发事件;
this作为sender传递(告诉订阅者是当前按钮触发的事件);
EventArgs.Empty是默认事件数据(无额外信息时使用)。
4. 订阅事件
csharp
loginBtn.Click += mainForm.Btn_Click; // 实例方法订阅
loginBtn.Click += LogManager.LogClickEvent; // 静态方法订阅
+=运算符:将方法添加到委托的“调用列表”中(多播委托);
实例方法vs静态方法:实例方法需要对象实例(如mainForm.Btn_Click),静态方法直接用类名调用(如LogManager.LogClickEvent);
多播特性:委托可以存储多个方法,触发时按添加顺序依次执行。
5. 取消订阅
csharp
loginBtn.Click -= LogManager.LogClickEvent;
-=运算符:将方法从委托的调用列表中移除,取消订阅后该方法不再接收事件通知。
基础知识拓展

  1. 委托vs事件核心区别
特性 委托(Delegate) 事件(Event)
本质 方法的引用类型 基于委托的成员(类的属性/字段)
访问权限 外部可直接赋值、调用 外部只能订阅(+=)/取消订阅(-=)
封装性 无封装,易被滥用 有封装,只能由类内部触发
适用场景 通用方法回调(如排序比较) 事件驱动通知(如按钮点击、消息接收)
  1. 多播委托的执行顺序
    委托的调用列表是一个方法数组,触发时按添加顺序依次执行。如果其中一个方法抛出异常,后续方法不会执行。例如:
    csharp
	// 假设订阅了MethodA和MethodB
	btn.Click += MethodA;
	btn.Click += MethodB;
	// 触发时先执行MethodA,再执行MethodB;若MethodA抛异常,MethodB不执行
  1. .NET中的通用委托(替代自定义委托)
    为了避免重复定义委托,.NET提供了通用委托类型:
    Action<T1,T2,...>:无返回值的委托,最多支持16个参数;
    Func<T1,T2,...,TR>:有返回值的委托,TR是返回类型;
    比如,我们可以用Action<object, EventArgs>代替自定义的ButtonClickHandler:
    csharp
    public event Action<object, EventArgs> Click; // 无需再定义委托
  2. 自定义事件数据(EventArgs子类)
    如果事件需要传递额外信息(如按钮点击时的坐标),可以继承EventArgs:
    csharp
	// 自定义事件数据
	public class ButtonClickEventArgs : EventArgs
	{
	public int X { get; set; } // 点击X坐标
	public int Y { get; set; } // 点击Y坐标
	}
	// 修改委托和事件
	public delegate void ButtonClickHandler(object sender, ButtonClickEventArgs e);
	public event ButtonClickHandler Click;
	// 触发事件时传递坐标
	protected virtual void OnClick(int x, int y)
	{
	Click?.Invoke(this, new ButtonClickEventArgs { X = x, Y = y });
	}

扩展思考
1.异常处理:在多播委托中,如果某个订阅者的方法抛出异常,如何保证后续方法继续执行?(提示:遍历委托的调用列表,逐个执行并捕获异常);
2.异步事件:如何让事件处理方法异步执行?(提示:用async/await修饰处理方法,或在触发时用Task.Run包装);
3.弱事件模式:如果订阅者的生命周期比触发者长,可能导致内存泄漏,如何用弱事件模式解决?(提示:使用WeakEventManager类);
4.通用委托应用:用Action代替自定义委托,修改按钮类的事件定义,验证功能是否正常。
总结
委托和事件是.NET事件驱动编程的核心:
委托:是方法的“容器”,实现方法的动态绑定;
事件:是委托的“安全封装”,确保事件只能由触发者内部触发,外部只能订阅/取消订阅;
.NET事件规范:sender+EventArgs的参数设计,让代码更规范、易维护。
通过按钮点击的模拟,你应该掌握了事件的全流程:定义委托→声明事件→订阅事件→触发事件→取消订阅。在实际开发中,事件广泛应用于UI交互、消息通知、状态变更等场景,是编写松耦合代码的关键技术。

本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49408.html
 

相关教程