目录

带参事件(EventHandler、泛型事件)

1.如何向事件传递“谁触发了事件(sender)

2.如何传递事件数据(如伤害值、坐标、状态等)

3.为什么 .NET 推荐使用 EventHandlerEventArgs

事件要传递“信息”怎么办

最基础的事件

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);

事件携带数据时用 EventArgs