你真的懂EventHandler移除吗?:一个被长期误解的技术盲区

第一章:你真的懂EventHandler移除吗?:一个被长期误解的技术盲区

在 .NET 开发中,事件(Event)是实现松耦合设计的核心机制之一。然而,关于如何正确移除事件处理器(EventHandler),许多开发者仍存在误解,导致内存泄漏或运行时异常。

常见误区:匿名方法与移除失败

当使用匿名方法或 Lambda 表达式订阅事件时,无法通过 -= 操作符正确移除事件处理器,因为每次创建的委托实例都是唯一的。

// 错误示例:无法移除的Lambda事件订阅
button.Click += (sender, e) => MessageBox.Show("Clicked");
button.Click -= (sender, e) => MessageBox.Show("Clicked"); // 不生效!
上述代码中,第二次 Lambda 生成的是新的委托实例,因此移除操作不会匹配原始订阅,导致事件依然存在引用。

正确的移除策略

要确保事件能被成功移除,必须持有对原始委托的引用。
  1. 将事件处理器定义为独立的方法
  2. 或使用变量保存 Lambda 委托引用

// 正确示例:通过变量持有委托引用
EventHandler clickHandler = null;
clickHandler = (sender, e) =>
{
    MessageBox.Show("Clicked");
    button.Click -= clickHandler; // 使用后立即移除
};
button.Click += clickHandler;
该方式确保了订阅与移除使用同一委托实例,避免内存泄漏。

事件生命周期管理建议

为降低风险,推荐以下实践:
  • 优先使用命名方法处理长期存在的事件
  • 在对象销毁前显式移除事件订阅
  • 考虑使用弱事件模式(Weak Event Pattern)防止宿主对象无法被回收
订阅方式可移除性推荐场景
命名方法✅ 易于移除控件事件、生命周期长的对象
Lambda(无引用)❌ 无法移除一次性短时操作
Lambda(变量引用)✅ 可控制移除需延迟解绑的场景

第二章:C#事件与多播委托的底层机制

2.1 事件本质探析:从IL看event关键字的封装

在C#中,event关键字是对委托(Delegate)的封装,提供“添加”和“移除”事件处理器的安全机制。通过反编译生成的IL代码,可发现编译器自动为事件生成了add_EventName和remove_EventName方法。

事件的IL结构解析
public event EventHandler MyEvent;

上述C#代码在IL层面被编译为一个私有委托字段,并附带两个特殊方法。CLR确保仅允许通过+=和-=操作安全地修改事件订阅,防止外部直接调用Invoke或赋值null导致状态不一致。

事件与委托的差异
  • 事件对外仅暴露订阅与取消机制
  • 委托字段可被外部直接调用或重置
  • 事件在类外无法被主动触发

2.2 多播委托链的结构与调用顺序解析

多播委托(Multicast Delegate)是C#中支持多个方法注册并依次调用的重要机制。其内部通过调用列表(Invocation List)维护一个方法指针链,每个节点指向一个可调用的方法。
调用列表的执行顺序
多播委托按订阅顺序同步执行所有方法,遵循“先订阅,先执行”的原则。若某个方法抛出异常,后续方法将不会被执行。
Action action = () => Console.WriteLine("第一步");
action += () => Console.WriteLine("第二步");
action(); // 输出:第一步 → 第二步
上述代码中,两个匿名方法按顺序加入调用链,执行时依次输出。
委托链的底层结构
每个Delegate实例包含Target(目标实例)和Method(方法信息),多播委托通过组合多个Delegate形成链表结构。
字段说明
Target方法所属的实例对象
Method具体的方法元数据

2.3 += 和 -= 操作符背后的委托实例合并与移除逻辑

在C#中,+= 和 -= 操作符不仅用于数值运算,更关键的是支持委托实例的动态合并与移除。当多个方法绑定到同一委托时,系统会构建一个调用链。
委托的合并机制
使用 += 可将新方法追加到委托链末尾:

Action action = () => Console.WriteLine("A");
action += () => Console.WriteLine("B"); // 合并
action(); // 输出 A 换行 B
上述代码中,两个匿名方法被合并为多播委托(MulticastDelegate),调用时按顺序执行。
移除逻辑与注意事项
-= 用于从链中移除指定方法引用。但必须注意:只有当初次赋值的实例完全匹配时才能成功移除。
  • 无法移除匿名方法的中间项
  • 重复添加同一方法会导致多次触发
  • 空委托调用不会抛异常

2.4 委托相等性判断:方法指针与目标实例的双重匹配

在 .NET 中,委托的相等性判断依赖于两个核心要素:目标实例(target instance)和方法指针(method pointer)。只有当两个委托指向同一对象实例的同一方法时,才会被视为相等。
委托相等性判定条件
  • 静态方法:仅需方法指针相同
  • 实例方法:目标实例与方法指针均需匹配
Action del1 = instance.Method;
Action del2 = instance.Method;
Console.WriteLine(del1 == del2); // 输出: True
上述代码中,del1 和 del2 共享相同的目标实例 instance 和方法 Method,因此相等性判断为真。若任一要素不同,即使逻辑行为一致,结果也为假。这种双重匹配机制确保了委托调用上下文的精确一致性。

2.5 移除失败的常见场景与调试技巧

权限不足导致移除失败
在执行资源删除操作时,最常见的问题是权限缺失。例如,在Kubernetes中删除命名空间时,若用户未被授予相应RBAC权限,操作将被拒绝。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: staging
  name: deleter-role
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["delete", "list"]
该Role定义允许在staging命名空间中删除Pod和服务。确保绑定至目标用户可避免权限类失败。
资源处于终态或被保护
某些资源因启用保护机制(如Kubernetes的Finalizer)无法立即删除。可通过查看状态字段定位阻塞原因:
  1. 检查资源描述信息:kubectl describe pod <name>
  2. 确认是否存在Finalizer列表
  3. 手动清除异常Finalizer(谨慎操作)

第三章:事件订阅泄漏的典型模式与后果

3.1 静态事件导致的对象生命周期延长问题

在 .NET 等支持事件机制的面向对象语言中,静态事件常被用于跨模块通信。但由于静态成员的生命周期贯穿整个应用程序域,订阅了静态事件的对象无法被正常释放,从而引发内存泄漏。
典型场景分析
当一个实例对象订阅静态事件后,事件持有对该实例方法的引用。即使该实例本应被回收,GC 仍因存在强引用而无法清理。

public static class EventBus
{
    public static event Action<string> OnDataReceived;

    public static void Raise(string data)
    {
        OnDataReceived?.Invoke(data);
    }
}

public class DataProcessor
{
    public DataProcessor()
    {
        EventBus.OnDataReceived += HandleData; // 订阅静态事件
    }

    private void HandleData(string data)
    {
        Console.WriteLine($"处理数据: {data}");
    }

    ~DataProcessor()
    {
        Console.WriteLine("DataProcessor 被销毁");
    }
}
上述代码中,DataProcessor 实例注册了静态事件 OnDataReceived,导致其生命周期与应用程序域绑定。即使外部不再引用该实例,也无法触发析构函数。
解决方案建议
  • 使用弱事件模式(Weak Event Pattern)解除强引用
  • 显式提供取消订阅机制,在对象销毁前调用
  • 考虑使用消息总线替代原始静态事件

3.2 匿名方法与闭包带来的移除困境

在事件处理机制中,匿名方法和闭包的使用极大提升了编码灵活性,但也引入了事件移除的难题。由于每次创建的匿名函数均为独立引用,无法通过常规方式匹配并解除订阅。
闭包导致的引用不一致
button.Click += (sender, e) => {
    Console.WriteLine("Clicked");
};
// 无法移除:无引用指向该委托实例
上述代码注册了一个匿名方法,但未保留其委托引用,导致后续无法调用 -= 操作符进行解绑,造成内存泄漏风险。
解决方案对比
  • 使用具名方法确保可移除性
  • 缓存闭包委托实例以供后续解绑
  • 借助弱事件模式缓解生命周期依赖
正确管理闭包生命周期是避免资源滞留的关键。

3.3 弱事件模式的必要性与适用场景

在长期运行的应用程序中,事件订阅若未妥善管理,极易引发内存泄漏。当事件发布者生命周期长于订阅者时,传统的强引用会导致订阅者无法被垃圾回收。
典型适用场景
  • WPF/Silverlight 中的 UI 控件事件绑定
  • 跨模块通信的事件总线系统
  • 服务层与 ViewModel 之间的状态通知
代码示例:弱事件实现机制
public class WeakEvent<TEventArgs>
{
    private readonly List<WeakReference> _listeners = new();

    public void Subscribe(object subscriber, Action<object, TEventArgs> callback)
    {
        _listeners.Add(new WeakReference(new SubscriberWrapper(subscriber, callback)));
    }

    public void Raise(object sender, TEventArgs args)
    {
        _listeners.RemoveAll(listener => !listener.IsAlive);
        foreach (var wrapper in _listeners.Cast<SubscriberWrapper>())
            wrapper.Invoke(sender, args);
    }
}
上述代码通过 WeakReference 包装订阅者,避免持有强引用。事件触发时自动清理已释放对象,有效防止内存泄漏。

第四章:安全移除事件的工程实践方案

4.1 显式命名方法 vs 匿名委托:可维护性对比

在 .NET 委托编程中,开发者常面临选择:使用显式命名方法还是匿名委托。这一决策直接影响代码的可读性与后期维护成本。
可读性与调试优势
显式命名方法通过清晰的方法名传达意图,便于调试和单元测试。例如:
public void RegisterCallback()
{
    Timer timer = new Timer(LogElapsedSeconds, null, 0, 1000);
}

private void LogElapsedSeconds(object state)
{
    Console.WriteLine($"Elapsed: {DateTime.Now}");
}
该方式方法名 LogElapsedSeconds 自文档化,调用栈清晰,利于问题追踪。
匿名委托的紧凑性与局限
匿名委托适用于简单逻辑,语法紧凑:
Timer timer = new Timer(state => Console.WriteLine("Tick"), null, 0, 500);
但缺乏命名语义,在多层嵌套时增加理解难度,且无法被重复调用或单独测试。
  • 命名方法:易于维护、重用、测试
  • 匿名委托:适合一次性、短小逻辑

4.2 手动解耦与中间代理类的设计模式应用

在复杂系统架构中,手动解耦是提升模块独立性的关键手段。通过引入中间代理类,可有效隔离核心业务逻辑与外部依赖。
代理类的基本结构

public class ServiceProxy implements IService {
    private RealService realService;

    public void execute(String data) {
        // 前置处理:日志、权限校验
        System.out.println("Proxy: Pre-processing");
        if (realService == null) {
            realService = new RealService();
        }
        realService.execute(data); // 转发调用
        System.out.println("Proxy: Post-processing");
    }
}
上述代码中,ServiceProxy 拦截客户端请求,在调用真实服务前后插入通用逻辑,实现关注点分离。
解耦优势对比
场景紧耦合代理解耦
维护成本高低
扩展性差良好

4.3 使用WeakEventManager实现无内存泄漏通信

在WPF和.NET事件模型中,长期存在的对象订阅短期对象的事件容易导致内存泄漏。传统的事件订阅会创建强引用,阻止垃圾回收。
WeakEventManager工作原理
该机制通过弱引用(WeakReference)监听事件源,避免持有目标对象的强引用。当监听对象被回收时,不会影响其生命周期。
代码实现示例

public class MyWeakEventManager : WeakEventManager
{
    private static MyWeakEventManager _instance;

    public static MyWeakEventManager Instance => 
        _instance ?? (_instance = new MyWeakEventManager());

    public void AddListener(INotifyPropertyChanged source, IWeakEventListener listener)
    {
        Listen(source, "PropertyChanged");
    }

    protected override void StartListening(object source)
    {
        ((INotifyPropertyChanged)source).PropertyChanged += OnEvent;
    }

    protected override void StopListening(object source)
    {
        ((INotifyPropertyChanged)source).PropertyChanged -= OnEvent;
    }
}
上述代码重写了StartListening和StopListening方法,注册和注销对PropertyChanged事件的弱监听。实例通过静态属性全局唯一,减少资源开销。

4.4 单元测试验证事件订阅状态的最佳实践

在微服务架构中,事件驱动的通信模式广泛使用,确保事件订阅逻辑正确至关重要。单元测试应覆盖订阅初始化、事件处理及异常恢复路径。
测试用例设计原则
  • 验证订阅器是否成功注册到事件总线
  • 确保事件触发时对应处理器被调用
  • 模拟网络异常或序列化失败,检验错误处理机制
代码示例:Go语言中基于Testify的订阅验证

func TestEventSubscriber_Register(t *testing.T) {
    bus := new(MockEventBus)
    handler := &OrderCreatedHandler{}
    subscriber := NewOrderSubscriber(bus, handler)

    subscriber.Subscribe()

    assert.True(t, bus.HasSubscription("OrderCreated"))
}
上述代码通过模拟事件总线,验证订单创建事件的订阅注册。MockEventBus用于隔离外部依赖,HasSubscription断言确保事件类型被正确监听,提升测试可重复性与稳定性。

第五章:结语:重新审视.NET中的事件管理哲学

在现代.NET应用架构中,事件不仅是通信机制,更是一种解耦设计的哲学体现。随着领域驱动设计(DDD)与微服务模式的普及,事件的管理方式已从简单的委托调用演变为跨服务、跨进程的异步消息流转。
事件驱动设计的实际落地挑战
在高并发场景下,直接使用内置的event EventHandler<T>可能导致内存泄漏或事件订阅失控。一个典型问题是未及时取消订阅:

// 危险示例:匿名方法导致无法取消订阅
publisher.DataReceived += (sender, e) => {
    Console.WriteLine(e.Value);
};
推荐做法是显式定义处理方法,便于生命周期管理:

private void OnDataReceived(object sender, DataEventArgs e)
{
    Console.WriteLine(e.Value);
}

// 订阅与取消
publisher.DataReceived += OnDataReceived;
publisher.DataReceived -= OnDataReceived; // 可控释放
从同步事件到异步流处理
对于需要保证顺序与可靠性的业务事件(如订单状态变更),应结合System.Threading.Channels构建异步管道:
  • 使用Channel<OrderEvent>作为事件缓冲队列
  • 后台消费者任务通过ReadAsync()持续处理
  • 支持背压(backpressure)机制,防止内存溢出
模式适用场景优点
委托事件UI交互、本地组件通信低延迟,语法简洁
Channels后台任务流处理支持异步流控
Message Broker分布式系统集成可靠传递,可扩展

事件源 → [Channel缓冲] → 处理器Worker → 持久化/通知

nvprof:CUDA编程性能分析工具 NVIDIA提供了具有高性能计算的GPU产品,如何检验硬件的实际性能,写出高效的CUDA代码让算力加速卡物尽其用,那可以通过其配套提供的“护城河”软件工具链来检验和帮助发挥GPU的性能:nvprof。如下,对于将8192个A,B向量进行向量加法运算,即C[i] = A[i] + B[i],当软件设置不同的blocknum, threadnum时候,CUDA执行kernel的时间是不同的。比如如下命令,可以看到程序tmp1的总体性能,下面一行是打印gpu的执行时间情况。 阅读详情

相关推荐

避开这些坑!智鼎MAP性格测试高分指南(附互联网/国企/外企答题模板)

本文深入解析智鼎MAP职业性格测试的核心逻辑与常见陷阱,提供针对互联网、国企、外企三大行业的差异化答题策略与实战模板。帮助求职者理解测试本质,避开追求完美人设、忽视程度副词等心理陷阱,塑造与目标岗位高度匹配的职业画像,从而在招聘关键环节脱颖而出。

rainy的博客 994

关于EventHandlerList的用法简介

EventHandlerList作为事件处理的替补方案 如果你发现你的服务组件对外需要提供很多的事件,而这些事件一般情况下你认为很少有程序拦截。使用EventHandlerList提供的功能将很适合你,如果使用.NET提供的默认事件机制,你可能在创建实例时消耗较多 的内存,而使用EventHandlerList挂接事件将节约内存。下面的代码演示了如何使用此功能。 private EventHa

脚踏实地 2612

利用Ghidra与SVD插件实现STM32固件深度逆向分析

本文详细介绍了如何利用Ghidra逆向工程框架结合SVD插件,实现对STM32固件(.bin文件)的深度逆向分析。通过加载描述芯片外设寄存器的SVD文件,Ghidra能将晦涩的硬件地址操作转换为可读的寄存器符号,极大提升了对嵌入式固件中通信协议、安全校验等关键逻辑的分析效率。文章涵盖了从Java环境搭建、工具配置到实战逆向解析的完整流程。

peach的博客 944

c# Event移除所有绑定,注销事件绑定

public delegate int MasterEventHandler(object dat); public event MasterEventHandler Event; /// &lt;summary&gt; /// /// &lt;/summary&gt; public void CLearAllEvent() { ...

a65135793的博客 1万+

DeepSeek 框架和技术栈分析

DeepSeek 诞生于对更高效、更强大的深度学习计算和模型训练的追求。随着深度学习模型的规模不断增大,如大型语言模型(LLMs)和复杂的视觉模型,传统的框架在处理这些大规模模型时面临着性能瓶颈和资源利用效率低下的问题。DeepSeek 旨在解决这些问题,通过优化框架设计和采用先进的技术栈,实现更高效的模型训练和推理,同时降低计算成本。

yournameplease的专栏 1512

【2023版】基于JT808/JT1078协议的车载视频监控平台开发实战-开源架构解析

本文详细解析了基于JT808/JT1078协议的车载视频监控平台开发实战,涵盖协议核心作用、开源架构设计、开发避坑指南及二次开发建议。重点介绍了JT1078协议在音视频传输中的优化方案,以及海量位置数据存储、高可用架构等关键技术点,为开发者提供全面的实战参考。

weixin_26965807的博客 236

EventHandlerList类型内部存储示意图

EventHandlerList提供一个简单的委托列表,该类使用线性搜索算法在委托列表中查找项。线性搜索算法在处理大量项时是低效的。因此当列表较大时,查找项的速度会比较慢,我们经常使用的事件委托,就是通过这个类型存储和调用的。用反射工具查看其代码 Code [HostProtection(SecurityAction.LinkDemand,SharedState=true)] public...

weixin_30662539的博客 137

励磁电感 Lm 选对了,LLC 才真“高效”:半桥 LLC 效率优化

你有没有遇到过这种 LLC:参数一套下来,ZVS 也有了,波形看着也“挺谐振”,但一上功率——,效率就是死活上不去。我说句直白的:LLC 的“高效”,很大一部分其实是在背后控场。Lm 选小了,励磁电流蹭蹭涨,谐振电流 RMS 变大,导通损耗、开关损耗、整流损耗一起上天;Lm 选太大,又可能把 ZVS 的门槛抬高,轻载掉 ZVS,EMI 也开始作妖。

weixin_43199439的博客 436

EventHandlerList 学习

EventCollection 如果在单个控件中有多个事件,那么使用System.ComponentModel.EventHandlerList对事件进行保存将会在内存占用上有不错的提高。EventHandlerList对一个类内发布多个事件提供了一个列表容器。下面是多个事件和使用EventHandlerList的对比示意: (汗这图看不懂什么意思) 第一步是实例化一个EventHand...

weixin_33872660的博客 262

大模型之Spring AI实战系列(二):Spring Boot + OpenAI 打造聊天应用全攻略

本篇文章是《Spring AI实战》专栏的第一篇开发入门文章,我们将借助一个完整的OpenAI聊天应用示例,详细介绍如何运用Spring Boot和Spring AI快速搭建一个支持OpenAI API的聊天服务,全面涵盖基础对话、流式对话、多提供商配置等核心功能。

寻道AI,探索AI无限可能! 6867

【硬件】LT3763中文手册

1)精确控制输入和输出电流;2) 3000:1真彩色PWM™调光;3) ±1.5%电压调节精度;4) ±6%电流调节精度;5)6V至60 V输入电压范围;6)宽输出范围最高可达55V;7)<2µA关断电流;8)用于负载电流热控制的控制引脚;9)输入和输出电流监控和限制;10)开路、短路和C/10故障检测;11)适用于LED应用的PWM驱动器输出;12)热增强型28引脚FE封装。

qq_39200110的博客 489

EventHandlerList的一些发现

EventHandlerList是用来管理委托、事件的一个集合,传说当一个控件有很多事件的时候将这些事件放到EventhandlerList里面可以大大的节省内存。 EventhandlerList常用的两个方法:AddHandler和RemoveHandler两个方法来对事件进行管理。 设有一个Custom类里面有一个funtion方法。 又定义了一个myDelegate的委托类型,它和事...

weixin_30834783的博客 172

LeRobot 入门教程(一)软件环境、部署流程及摄像头配置

🤖 LeRobot:开源机器人学习平台 摘要:LeRobot是一个基于PyTorch的开源机器人学习平台,提供模型、数据集和工具,旨在降低机器人技术门槛。平台包含经过验证的模仿学习和强化学习方法,并提供预训练模型、人类演示数据集及模拟环境。安装需从源码获取,支持Python 3.10环境,可选扩展包括仿真环境(如gym-aloha)、电机控制(Dynamixel/Feetech SDK)和实验跟踪(Weights & Biases)。该平台托管在HuggingFace,并设有社区Discord频道

2506_90492529的博客 3417

EventHandlerList

publicsealedclassEventHandlerList:IDisposable{//FieldsprivateListEntryhead;privateComponentparent;//MethodspublicEventHandlerList();internalEventHandlerList(C...

weixin_34177064的博客 144

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

EventHandlerList 类

提供一个简单的委托列表。无法继承此类。 要使用事件属性(Visual Basic 2005 中的自定义事件),请在引发事件的类中定义事件属性,然后在处理事件的类中设置事件属性的委托。要在一个类中实现多个事件属性,该类必须在内部存储和维护为每个事件定义的委托。一种典型方法是实现通过事件键进行索引的委托集合。 若要存储每个事件的委托,可以使用 EventHandlerList 类或实现您自己...

weixin_30781631的博客 213

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C# EventHandler委托事件小结

最近遇到一个委托的问题,+=这个符号,哇,真的是头皮发麻,以为是运算符,根本不知道是委托 -。-!!看下图: 后面查了以后原来这种用法真的很普遍,我真是小白~~ 资料中对C#委托的小清新解释:this.Activated += new EventHandler(Form1_Activated); //Form1_Activated为方法名这个语句拆开为:this.Activated=先执行;

xjm1027932743的博客 9740

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Eventhandler机制

1) Eventhandle初始化: vim frameworks/base/core/java/android/hardware/Camera.java private int cameraInitVersion(int cameraId, int halVersion) {     mShutterCallback = null;     mRawImageCallback = null;  ...

产品线负责人 2824

C# 移除所有的事件绑定

private delegate int DEL_TEST_EventHandler(int m, int n); private event DEL_TEST_EventHandler DelTestEventHandler; /// /// 移除所有的事件绑定 /// /// private void

qingshuiaishui的专栏 9172

C#中移除自定义事件的所有方法

/// /// 移除一个对象指定事件的所有注册的方法 /// /// 泛型 /// 当前对象 /// 事件名 public static void RemoveEvent(T obj, string eventName) { Delegate[] invoke

soapcoder92的博客 3430

对事件触发(EventHandler)的简单理解

当某个对象的某个事件或者是执行过程发生了,这个对象可以通过事件通知另外一个对象,另外一个对象可以通过传递进来的参数进行相应的处理; 网上有很多关于事件触发的例子,借鉴其中的一个例子对事件触发做如下理解: 定义事件触发需要如下3步 一、事件参数Class定义 定义事件的参数 二、事件Class定义 1、定义一个事件对象 2、定义可以触发此事件的函数 三、使用Class定义     1...

u011805979的博客 4万+

C# 清除事件绑定的函数

原文地址:点击打开链接 #region 清除事件绑定的函数 /// <summary> /// 清除事件绑定的函数 /// </summary> /// <param name="objectHasEvents">拥有事件的实例</param&...

weixin_33694620的博客 1100

C# 移除事件

去除事件public static class EventExtension{ public static void RemoveEvents<T>(this Control target,string Event) { FieldInfo f1 = typeof(Control).GetField(Event,BindingFlags.Static ...

weixin_34401479的博客 905

奇怪的匿名函数之争EventHandler

class Sample { //event EventHandler clickSample= delegate { };//这种方式也行 event EventHandler clickSample = delegate(object sender,EventArgs e) { }; public static void Main(...

weixin_30807677的博客 179

游戏开发-cocos creator技巧-cc.Component.EventHandler自定义click事件

cocos creator有两种给按钮注册click事件的方式, EventHandler的方式虽然有点麻烦,但是比较灵活,使用时候注意每个参数必须填写正确,否则click时候会报null的错误 第一种 node_level.on(cc.Node.EventType.TOUCH_START, this.on_pass_entry_click, this); 第二种 ...

weixin_30240349的博客 1356
上一篇: 为什么你的结构体比较总是错的?,揭秘Equals重写的3个隐藏规则
下一篇: 【PHP字符串处理核心技巧】:strstr与stristr的5大区别及应用场景详解
CodeIsle
博客等级 码龄1年 145粉丝 1966原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值