【问题标题】:Referencing asynchronous tasks in a c# dictionary在 C# 字典中引用异步任务
【发布时间】:2016-01-15 04:30:46
【问题描述】:

我现在在几个程序的字典中遇到了关于异步任务的问题,我不知道如何解决它。

我有一个如下所示的异步任务:

MessageEventArgs.Channel.SendMessage("somestring");

MessageEventArgs 是我在程序开始时静态声明的第 3 方库中的一个类:

public static MessageEventArgs meargs;

程序侦听 IRC 频道中的事件并根据文本命令执行操作。我不想为每个命令创建一个巨大的 switch 语句,而是制作一个将字符串与方法匹配的字典。并非所有人都只是简单地发送消息,所以我不能只存储要发送的字符串。它看起来像这样:

public static Dictionary<string, Task> myDict= new Dictionary<string, Task>()
{
    {"!example", MessageEventArgs.Channel.SendMessage("HelloWorld") }
}

在 Main() 中,我调用:

MessageReceived += async (s,e) => 
{
     meargs = e;
     await myDict[e.Message.Text];
}

我开始意识到,当字典被实例化时,它会尝试调用 SendMessage,因为它是一个异步任务。 MessageEventArgs 直到之后才实例化,因此它会引发异常。有没有办法在字典中存储对这些函数的引用?我找到了使用委托的解决方案,但它们似乎不适用于void 方法,并且async 方法(据我所知)只能返回voidTask

提前致谢!

【问题讨论】:

  • 您在初始化 myDict 时是否尝试过“新建”SendMessage 实例?
  • 您确定应该将MessageEventArgs 保留为像这样的静态变量吗?它的名字让我觉得它是EventArgs 的子类,每次事件发生时都会发送给你。我从来没有真正遇到过将它们作为长期存在的实例保留是个好主意的情况。
  • @JustinR.我没试过,但我会试一试。
  • @BradleyUffner 实际上,你是对的。完全错过了。我会摆脱它并找到另一种方式来调用 SendMessage
  • 任务的“动作”签名——即任务实际执行的方法——是否对所有任务都相同?如果有,请分享。

标签: c# async-await task


【解决方案1】:

不要立即创建任务,而是存储工厂:

public static Dictionary<string, Func<Task>> myDict= new Dictionary<string, Func<Task>>()
{
    {"!example", () => MessageEventArgs.Channel.SendMessage("HelloWorld") }
}

await myDict[e.Message.Text]();

【讨论】:

    【解决方案2】:

    如果我正在写这篇文章,我会创建一个带有虚拟ExecuteCommand 函数的IrcCommand 类。然后为我希望能够运行的每个不同命令创建一个子类。每个子类都将使用调用命令所需的逻辑覆盖ExecuteCommand 函数。 ExecuteCommand 可以采用Channel 类型的参数,对应于调用命令的通道。

    在启动时,我会创建一个Dictionary&lt;string, IrcCommand&gt;,其中填充了命令文本的Key,以及实现该命令的子类的新实例Value

    当一个命令进来时,我会使用匹配的键从字典中拉出IrcCommand 实例,并在其上调用ExecuteCommand 函数,传入接收命令的Channel


    可能会发生这样的事情。我实际上没有打开 Visual Studio 来验证我的语法,所以这里可能有一些错误。

    一些命令的实现:

    public class ExampleCommand : IrcCommand
    {
        public override void ExecuteCommand(Channel channel)
        {
            channel.SendMessage("Hello World");
        }
    }
    
    public class DisconnectCommand : IrcCommand
    {
        public override void ExecuteCommand(Channel channel)
        {
            channel.Disconnect();
        }
    }
    

    在应用程序启动时注册命令:

    private void RegisterCommands()
    {
        _commands.add("!example", new ExampleCommand());
        _commands.add("!disconnect", new DisconnectCommand());
    }
    

    收到后执行命令:

    _commands(commandText).ExecuteCommand(e.channel);
    

    【讨论】:

    • 我添加了更多示例代码,包括一个不仅仅发送文本的命令,这应该让您更好地了解我在说什么。
    • 这太完美了。设置它需要做更多的工作,但我的 Program.cs 现在干净多了,这将使将来添加新命令变得容易。谢谢!
    • 请注意这个实现,不要在个人IrcCommand 中存储任何类型的状态。由于每个命令只有一个实例,因此它们都将共享该状态。如果您需要存储状态,可以修改该模式,以便命令字典存储要执行的 IrcCommandType,然后您将使用反射,特别是 Activator.CreateInstance 每次创建一个新的命令实例。
    猜你喜欢
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-12
    相关资源
    最近更新 更多