C产品如何满足特定用户需求?
- 内容介绍
- 文章标签
- 相关推荐
本文共计890个文字,预计阅读时间需要4分钟。
在C语言中,以下是一个简单的示例代码,用于输出在+C:
为什么 event 是首选,而不是裸委托或手写订阅列表
event 是 C# 编译器提供的语法糖,它把委托字段封装起来,只暴露 += 和 -= 操作符。这带来三个实际好处:
- 外部代码无法清空整个委托链(比如
OnDataChanged = null;),避免意外破坏通知机制 - 不能直接调用事件(
OnDataChanged(...)会编译报错),强制你走?.Invoke()路径,天然带空值防护 - 和 WinForms/WPF/ASP.NET 等框架事件系统完全兼容,后续接入命令绑定、MVVM 框架时零迁移成本
常见错误是声明 public Action<string> OnDataChanged;</string> —— 这不是观察者模式,这是“把委托当公共字段用”,极易引发订阅被覆盖、调用时崩溃等问题。
EventHandler 比 Action 更靠谱的三个理由
别图省事用 Action<string></string>,优先选 EventHandler<datachangedeventargs></datachangedeventargs>:
-
sender参数能明确区分事件来源:同一个处理方法订阅了多个对象时,靠sender才能知道是谁发的通知 - .NET 生态默认约定,所有控件事件(
Button.Click、TextBox.TextChanged)都遵循这个签名,保持一致性 - 事件参数建议用不可变类(
record或readonly struct),比如:public record DataChangedEventArgs(string Value, bool IsUrgent);避免订阅者改了参数影响其他监听方
触发事件时必须写的那行防御代码
永远用 DataChanged?.Invoke(this, args);,不要写:
if (DataChanged != null) DataChanged(this, args);
原因:多线程下,判空和调用之间可能有其他线程执行 -=,导致 NullReferenceException。而 ?.Invoke() 是原子操作,.NET 6+ 还会自动做空值跳过。
如果发布逻辑本身跨线程(比如后台任务触发 UI 事件),需额外加锁或切回 UI 线程(Dispatcher.Invoke / SynchronizationContext.Post),但这是调度问题,不是事件机制本身的问题。
什么时候该放弃 event,转用 IObservable
event 不是万能的。当你遇到这些情况时,说明它已经不够用了:
- 需要对通知流做
.Where()、.Throttle()、.Retry()等组合变换 - 要统一管理多个数据源(比如传感器 + API + 用户输入)并合并成单一流
- 下游必须能感知错误(
OnError)或完成(OnCompleted),而不仅仅是收到数据
这时才考虑 IObservable<t></t>,且优先用 Observable.FromEventPattern() 封装现有 event,而不是从头手写 Subject<t></t> —— 后者极易漏掉 Dispose 清理,造成内存泄漏。
真正容易被忽略的是生命周期:event 订阅不返回资源句柄,而 IObservable<t>.Subscribe()</t> 返回 IDisposable,漏掉 .Dispose() 就等于泄漏订阅者引用。
本文共计890个文字,预计阅读时间需要4分钟。
在C语言中,以下是一个简单的示例代码,用于输出在+C:
为什么 event 是首选,而不是裸委托或手写订阅列表
event 是 C# 编译器提供的语法糖,它把委托字段封装起来,只暴露 += 和 -= 操作符。这带来三个实际好处:
- 外部代码无法清空整个委托链(比如
OnDataChanged = null;),避免意外破坏通知机制 - 不能直接调用事件(
OnDataChanged(...)会编译报错),强制你走?.Invoke()路径,天然带空值防护 - 和 WinForms/WPF/ASP.NET 等框架事件系统完全兼容,后续接入命令绑定、MVVM 框架时零迁移成本
常见错误是声明 public Action<string> OnDataChanged;</string> —— 这不是观察者模式,这是“把委托当公共字段用”,极易引发订阅被覆盖、调用时崩溃等问题。
EventHandler 比 Action 更靠谱的三个理由
别图省事用 Action<string></string>,优先选 EventHandler<datachangedeventargs></datachangedeventargs>:
-
sender参数能明确区分事件来源:同一个处理方法订阅了多个对象时,靠sender才能知道是谁发的通知 - .NET 生态默认约定,所有控件事件(
Button.Click、TextBox.TextChanged)都遵循这个签名,保持一致性 - 事件参数建议用不可变类(
record或readonly struct),比如:public record DataChangedEventArgs(string Value, bool IsUrgent);避免订阅者改了参数影响其他监听方
触发事件时必须写的那行防御代码
永远用 DataChanged?.Invoke(this, args);,不要写:
if (DataChanged != null) DataChanged(this, args);
原因:多线程下,判空和调用之间可能有其他线程执行 -=,导致 NullReferenceException。而 ?.Invoke() 是原子操作,.NET 6+ 还会自动做空值跳过。
如果发布逻辑本身跨线程(比如后台任务触发 UI 事件),需额外加锁或切回 UI 线程(Dispatcher.Invoke / SynchronizationContext.Post),但这是调度问题,不是事件机制本身的问题。
什么时候该放弃 event,转用 IObservable
event 不是万能的。当你遇到这些情况时,说明它已经不够用了:
- 需要对通知流做
.Where()、.Throttle()、.Retry()等组合变换 - 要统一管理多个数据源(比如传感器 + API + 用户输入)并合并成单一流
- 下游必须能感知错误(
OnError)或完成(OnCompleted),而不仅仅是收到数据
这时才考虑 IObservable<t></t>,且优先用 Observable.FromEventPattern() 封装现有 event,而不是从头手写 Subject<t></t> —— 后者极易漏掉 Dispose 清理,造成内存泄漏。
真正容易被忽略的是生命周期:event 订阅不返回资源句柄,而 IObservable<t>.Subscribe()</t> 返回 IDisposable,漏掉 .Dispose() 就等于泄漏订阅者引用。

