【问题标题】:How wire up an async delegate to an event如何将异步委托连接到事件
【发布时间】:2020-11-30 07:56:30
【问题描述】:

在我开始之前,我在这里能找到的最接近的答案是 How do you implement an async action delegate method? 但是,作为一个有点小脑袋的熊,我无法让我的脑细胞围绕答案以及它如何适用于我当前的问题。

我正在围绕 RabbitMq C# 客户端编写一个包装器,但我的委托和异步事​​件有问题。

这段代码看起来很好:

private Action<object, BasicAckEventArgs> _syncHandlerBasicAck;
private Action<object, BasicNackEventArgs> _syncHandlerBasicNack;

在这里定义代码:

_syncHandlerBasicAck = delegate(object o, BasicAckEventArgs args)
{
    //[Do stuff here]
};

_syncHandlerBasicNack = delegate(object o, BasicNackEventArgs args)
{
    //[Do stuff here]
};

然后使用它:

Channel.BasicAcks += _syncHandlerBasicAck.Invoke;
Channel.BasicNacks += _syncHandlerBasicNack.Invoke;

我遇到的问题是异步 BasicDeliver 事件。这是我当前的代码:

private Func<object, BasicDeliverEventArgs, Task> _syncHandlerIncomingMessage;

紧随其后

_syncHandlerIncomingMessage = async delegate(object o, BasicDeliverEventArgs args)
{
    //[Do stuff here]
};

但是每当我尝试这样做时:

Consumer.Received += _syncHandlerIncomingMessage.Invoke;

我收到 System.NullReferenceException 告诉我

对象引用未设置为对象的实例。

我看到有一个可用的 BeginInvoke 方法,但我不确定这是否适用于我目前的情况,因为我没有使用回调,只是想异步调用它。

有人指点吗? (我什至会采用“重复问题:在此处回答”类型的回复,因为我确信我可能遗漏了什么)。

编辑

我有两个问题;一个是把它连接起来,另一个是一些重构妨碍了我!非常感谢@Coolbots 的回答和@Camilo 向我展示我的方式的错误。

【问题讨论】:

  • 我对您订阅事件的方式感到困惑。你能不做Channel.BasicAcks += (object o, BasicAckEventArgs args) =&gt; { //[Do stuff here] }; 和/或Channel.BasicAcks += async (object o, BasicAckEventArgs args) =&gt; { //[Do stuff here] };吗?
  • 我正在尝试创建一些更简洁的代码@GSerg。我最初是这样的,我的外部方法变得有点毛茸茸。也就是说,我仍然想知道您如何做到这一点,因为在我看来,您应该能够做到这一点。
  • 您的async delegate 可以是返回async Task 的自己的方法(而不是存储在_syncHandlerIncomingMessage 中的匿名方法),并将该方法分配给Consumer.Received
  • 想用答案扩展它,@CoolBots 吗?
  • 我不认为错误来自发布的代码。我刚刚在控制台应用程序(没有 RabbitMQ 依赖项)上对此进行了测试,它适用于 Consumer.Received += x.Invoke;

标签: c# async-await


【解决方案1】:

问题是事件订阅不直接支持async delegate,所以需要一个包装器,像这样:

替代语法(参见EDIT):

async Task MyDelegate(object sender, BasicDeliverEventArgs e)
{
   // Do stuff 
} 

Consumer.Received += async (s, e) => await MyDelegate(s, e);

我正在使用手机(无法测试代码),所以虽然我确定这适用于 async Task MyDelegate(...),但我不是 100% 这适用于 async delegate MyDelegate(...);虽然理论上应该。

编辑:删除,然后根据 OP 的要求恢复我的答案(我很高兴它具有一定的价值);终于到了一台电脑上,并验证了所有 3 种订阅方法都有效,正如@CamiloTerevinto 在评论中所验证的那样,以及我最初删除这个答案的原因:

Func<object, BasicDeliverEventArgs, Task> _syncHandlerIncomingMessage;

_syncHandlerIncomingMessage = async delegate (object sender, BasicDeliverEventArgs eventArgs)
{
   await Task.Delay(1000); // Do stuff
};

// OR

async Task SyncHandlerIncomingMessage(object sender, BasicDeliverEventArgs eventArgs)
{
   await Task.Delay(1000); // Do stuff
} 

// Can be subscribed as follows:

Consumer.Received += _syncHandlerIncomingMessage.Invoke; // as posted by OP

// OR

Consumer.Received += async (s, e) => await SyncHanlderIncomingMessage.Invoke(s, e);

// OR

Consumer.Received += SyncHandlerIncomingMessage;

总结:不需要包装器。我很高兴我最初的答案对 OP 很有价值;我正在添加此编辑以确保答案的完整性/正确性,希望对其他人有用。

【讨论】:

  • 同样的错误:Consumer.Received += async (s, e) =&gt; await _syncHandlerIncomingMessage(s, e);
  • Consumer.Received += async (s, e) =&gt; await _syncHandlerIncomingMessage.Invoke(s, e); 仍然没有骰子 - 仍然是同样的错误
  • 好的,那么async delegate 似乎不可能。我会更新我的答案。
  • @CoolBots 您在回答中显示的是其他方法,但它仍然可以如问题代码中所示工作(在带有延迟和消息的控制台应用程序中测试)
  • @CoolBots 我认为我们并不了解对方。我的意思是事件注册有效,OP的问题来自另一个地方
猜你喜欢
  • 1970-01-01
  • 2015-08-02
  • 2017-06-23
  • 2010-11-27
  • 2010-09-26
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多