带参事件(EventHandler、泛型事件)
目录
1.如何向事件传递“谁触发了事件(sender)
2.如何传递事件数据(如伤害值、坐标、状态等)
3.为什么 .NET 推荐使用 EventHandler和 EventArgs
事件要传递“信息”怎么办
最基础的事件
public event Action OnSomething;这种事件只能“通知”,不能携带数据。
但是在真实系统中,事件往往需要传递信息,例如:
- 谁触发了这个事件?
- 触发时有什么数据?
- 伤害是多少?
- 哪个用户登录了?
- 哪个按钮被点击?
武侠比喻:
门派报名册收到通知时,需要告诉你:
哪个门派来报到了?
带了多少人?
有没有礼物?
1.事件经典签名
.NET 推荐的事件签名永远是
void Handler(object? sender, EventArgs e)也就是说所有事件方法应该长这样:
void OnSomethingHappened(object? sender, Eventargs e);2.为什么这样设计?
因为 Windows, WPF, WinForm, ASP.NET, Unity…
所有.NET 的生态都是这么设计的
事件参数的标准模式:
- sender: 谁触发事件(哪个门派发的信)
- EventArgs e: 事件参数 (随信带的礼物 )
3.定义一个带参数的事件
3.1 定义事件参数
public class ArriveEventArgs : EventArgs
{
public string SectName { get; set; }
public int MemberCount { get; set;}
}3.2 定义事件
public event EventHandler<ArriveEventArgs> 大会报到;3.3 触发事件(盟主内部调用)
大会报道?.Invoke(
this,
new ArriveEventArgs
{
SectName = "少林寺",
MemberCount = 10
}
);3.4 门派订阅
world.大会报道 += (sender ,e) =>
{
Console.WriteLine($"{e.SectName} 带了 {e.MemberCount}人来报到!");
};完整demo1
class Program
{
static void Main()
{
var world = new MartialWorld();
world.大会报到 += (sender, e) =>
{
Console.WriteLine($"{e.SectName} 来了,人数: {e.MemberCount}");
};
world.Arrive("少林寺", 10);
world.Arrive("武当派", 6);
}
}
public class ArriveEventArgs : EventArgs
{
public string SectName { get; set; }
public int MemberCount { get; set;}
}
public class MartialWorld
{
public event EventHandler<ArriveEventArgs> 大会报到;
public void Arrive(string sect, int count)
{
大会报到?.Invoke(this, new ArriveEventArgs
{
SectName = sect,
MemberCount = count
});
}
}demo2
internal class Program
{
static void Main(string[] args)
{
MartialWorld world = new MartialWorld();
world.ConferenceReport += World_ConferenceReport;
world.Arrive("shaolin", 10);
world.Arrive("wudang", 6);
}
// 标准事件处理方法:包含 sender 和事件参数
private static void World_ConferenceReport(object sender, ArriveEventArgs e)
{
Console.WriteLine($"{e.SectName} is comming, count: {e.MemberCount}");
}
}
// 事件参数类
public class ArriveEventArgs : EventArgs
{
public ArriveEventArgs(string sectName, int memberCount)
{
SectName = sectName;
MemberCount = memberCount;
}
public string SectName { get; }
public int MemberCount { get; }
}
// 发布事件类
public class MartialWorld
{
public event EventHandler<ArriveEventArgs> ConferenceReport;
// 标准事件触发器(通过受保护的虚方法触发:1.子类可以 override 来扩展行为 2.避免重复触发逻辑)
protected virtual void OnConferenceReport(ArriveEventArgs e)
{
ConferenceReport?.Invoke(this, e);
}
// 某个门派到来 -> 触发事件
public void Arrive(string sect, int count)
{
OnConferenceReport(new ArriveEventArgs(sect, count));
}
}demo2扩展(子类扩展)
internal class Program
{
static void Main(string[] args)
{
MartialWorld world = new StrictMartialWorld();
world.ConferenceReport += World_ConferenceReport;
world.Arrive("shaolin", 10);
world.Arrive("wudang", 3);
}
// 标准事件处理方法:包含 sender 和事件参数
private static void World_ConferenceReport(object sender, ArriveEventArgs e)
{
Console.WriteLine($"{e.SectName} is comming, count: {e.MemberCount}");
}
}
// 事件参数类
public class ArriveEventArgs : EventArgs
{
public ArriveEventArgs(string sectName, int memberCount)
{
SectName = sectName;
MemberCount = memberCount;
}
public string SectName { get; }
public int MemberCount { get; }
}
// ------------------- 基类 -------------------
public class MartialWorld
{
public event EventHandler<ArriveEventArgs> ConferenceReport;
protected virtual void OnConferenceReport(ArriveEventArgs e)
{
ConferenceReport?.Invoke(this, e);
}
public void Arrive(string sect, int count)
{
OnConferenceReport(new ArriveEventArgs(sect, count));
}
}
// ------------------- 子类(扩展事件行为) -------------------
public class StrictMartialWorld : MartialWorld
{
protected override void OnConferenceReport(ArriveEventArgs e)
{
// 子类扩展逻辑: 小门派不准参加会议
if (e.MemberCount < 5)
{
Console.WriteLine($"[BLOCKED] {e.SectName} too few people, denied entry");
return; // 阻止触发事件
}
Console.WriteLine($"[CHECK PASS] {e.SectName} pass the headcount check");
// 调用父类的触发逻辑 -> 事件正常分发给订阅者
base.OnConferenceReport(e);
}
}总结:
事件参数总是 EventArgs 的子类(行业标准,所有 UI 框架都这样)
事件签名一般是:
EventHandler
EventHandler<TEventArgs>事件触发只能在类内部:
大会报到?.Invoke(this, args);