首页 > 解决方案 > 什么是 F# 的标准类型事件?

问题描述

我正在尝试从 F# 订阅/取消订阅 C# 制作的(在 RabbitMQ 客户端库中):

AsyncEventingBasicConsumer.cs

public event AsyncEventHandler<BasicDeliverEventArgs> Received;

AsyncEventHandler.cs

public delegate Task AsyncEventHandler<in TEvent>(object sender, TEvent @event) where TEvent : EventArgs;

我设法通过执行以下操作订阅了该事件:

let subscribeAsync (channel: IModel) exchange callback =
    let consumer = AsyncEventingBasicConsumer(channel)
    consumer.add_Received(AsyncEventHandler<BasicDeliverEventArgs>(fun sender args -> Task.CompletedTask))
    // ...

话虽如此,我想知道为什么下面的代码无法编译:

let subscribeAsync (channel: IModel) exchange callback =
    let consumer = AsyncEventingBasicConsumer(channel)
    consumer.Received.AddHandler(AsyncEventHandler<BasicDeliverEventArgs>(fun sender args -> Task.CompletedTask))
    // ...

因为我收到以下错误:

Program.fs(10, 14): [FS1091] The event 'Received' has a non-standard type. If this event is declared in another CLI language, you may need to access this event using the explicit add_Received and remove_Received methods for the event. If this event is declared in F#, make the type of the event an instantiation of either 'IDelegateEvent<_>' or 'IEvent<_,_>'.

我查看了官方的 MS 文档,但看不到任何关于 F# 标准事件类型的参考。

标签: c#.net-coref#rabbitmqevent-handling

解决方案


F# 会将 .NET 事件公开为IEvent或类型的值IDelegateEvent,具体取决于事件声明和委托类型的外观。这只能针对具有一些基本通用结构的事件执行 - 当 F# 无法执行此操作时,它会将事件的底层addremove操作公开为您可以直接调用的方法。

我不确定什么是“标准事件类型”的规则是什么。但是,您可以从F# 编译器源代码的相关位中获得一些提示:

let TryDestStandardDelegateType (infoReader: InfoReader) m ad delTy =
    let g = infoReader.g
    let (SigOfFunctionForDelegate(_, compiledViewOfDelArgTys, delRetTy, _)) =
        GetSigOfFunctionForDelegate infoReader delTy m ad
    match compiledViewOfDelArgTys with 
    | senderTy :: argTys when (isObjTy g senderTy) && 
         not (List.exists (isByrefTy g) argTys)  -> Some(mkRefTupledTy g argTys, delRetTy)
    | _ -> None

所以,我的猜测是“标准事件类型”需要:

  • 至少有一个参数
  • 第一个参数必须是类型object
  • 没有byref参数

推荐阅读