如何在F#中定义与C#完全一致的事件以适配第三方应用?
问题描述
我在C#中有如下事件定义,这是第三方应用(TPA)要求的格式——该应用会导入包含此类定义的DLL并以图形化方式展示字段:
public event Action<Unit> Output1;
我尝试使用F#官方给出的等价写法定义:
let output1 = Event<Unit>() [<CLIEvent>] member __.Output1 = output1.Publish
但TPA无法识别这个F#定义。通过ILSpy对比编译后的IL代码,发现两者实现存在明显差异:
- C#编译后的IL代码:
public event Action<Unit> Output1; - F#编译后的IL代码:
[CLIEvent] public event FSharpHandler<Unit> Output1 { add { if (init@16 < 1) { LanguagePrimitives.IntrinsicFunctions.FailInit(); } output1.Publish.AddHandler(value); } remove { if (init@16 < 1) { LanguagePrimitives.IntrinsicFunctions.FailInit(); } output1.Publish.RemoveHandler(value); } }
显然是这种差异导致TPA无法识别,请问有没有办法在F#中定义该成员,让编译结果与C#版本完全一致?
解决方案
可以通过显式声明事件的委托类型为Action<Unit>,并手动实现对齐C#默认逻辑的add/remove方法,消除F#特有的包装和检查:
线程安全版本(与C#自动事件完全一致)
open System open System.Threading type MyClass() = // 私有委托字段,存储事件订阅者 let mutable output1Handler : Action<Unit> = null [<CLIEvent>] member this.Output1 with add handler = // 线程安全的委托合并逻辑,和C#编译器生成的代码对齐 let mutable current = output1Handler while true do let newHandler = Action.Combine(current, handler) let old = Interlocked.CompareExchange(&output1Handler, newHandler, current) if old = current then break else current <- old and remove handler = // 线程安全的委托移除逻辑,和C#编译器生成的代码对齐 let mutable current = output1Handler while true do let newHandler = Action.Remove(current, handler) let old = Interlocked.CompareExchange(&output1Handler, newHandler, current) if old = current then break else current <- old
这个实现的编译结果和C#自动事件完全匹配:
- 事件类型为
Action<Unit>,无F#特有的FSharpHandler包装 - add/remove方法使用线程安全的委托更新逻辑
- 不存在F#特有的初始化检查代码
简化非线程安全版本
如果TPA不要求线程安全,也可以使用更简洁的实现:
open System type MyClass() = let mutable output1Handler : Action<Unit> = null [<CLIEvent>] member this.Output1 with add handler = output1Handler <- Action.Combine(output1Handler, handler) and remove handler = output1Handler <- Action.Remove(output1Handler, handler)
内容的提问来源于stack exchange,提问作者Franco Tiveron
相关产品推荐
相关产品推荐

