Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Referencing asynchronous tasks in a c# dictionary

I've come across the problem regarding asynchronous tasks in a dictionary in a couple programs now, and I can't wrap my head around how to solve it.

I have an asynchronous task that looks like this:

MessageEventArgs.Channel.SendMessage("somestring");

MessageEventArgs is a class from a 3rd party library that I declare statically at the start of my program:

public static MessageEventArgs meargs;

The program listens for events in an IRC channel and does actions based on text commands. Rather than have a giant switch statement for each command, I wanted to make a dictionary that matched the string to the method. Not all are simply sending messages, so I can't just store the string to send. It looks like this:

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

In Main(), I call:

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

What I've come to realize is that when the dictionary is instantiated, it tries to call SendMessage, as it is an async task. MessageEventArgs isn't instantiated until after, so it throws an exception. Is there a way to go about storing a reference to these functions in a dictionary? I found solutions using delegates, but they didn't seem to work with void methods, and async methods (to my knowledge) can only return void or Task.

Thanks in advance!

like image 927
Mason Avatar asked Jan 14 '16 18:01

Mason


People also ask

What is asynchronous task in C#?

C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. It follows what is known as the Task-based Asynchronous Pattern (TAP).

How do you run asynchronous tasks?

Async/Await on a Separate Thread If a predefined method returns a Task , you simply mark the calling method as async and put the await keyword in front of the method call. It's helpful to understand the control flow associated with the await keyword, but that's basically it.

How do you call async task bool?

To return Boolean from Task Synchronously, we can use Task. FromResult<TResult>(TResult) Method. This method creates a Task result that's completed successfully with the specified result. The calling method uses an await operator to suspend the caller's completion till called async method has finished successfully.

Can we use async void in C#?

You use the void return type in asynchronous event handlers, which require a void return type. For methods other than event handlers that don't return a value, you should return a Task instead, because an async method that returns void can't be awaited.


2 Answers

Instead of creating the tasks immediately, store factories:

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

await myDict[e.Message.Text]();
like image 133
usr Avatar answered Oct 12 '22 23:10

usr


If I were writing this, I would create an IrcCommand class with a virtual ExecuteCommand function. Then create a subclass for every distinct command I wanted to be able to run. Each subclass would override the ExecuteCommand function with the logic needed to invoke the command. ExecuteCommand could take an argument of type Channel corresponding the the channel to invoke the command.

At startup I would create a Dictionary<string, IrcCommand> that was populated with a Key of the command text, and a Value of a new instance of the subclass implementing that command.

When a command comes in I would pull the IrcCommand instance from the dictionary with the matching key and call the ExecuteCommand function on it, passing in the Channel the command was received from.


It could happen something like this. I don't actually have visual studio open to verify my syntax, so I may have a few errors in here.

An implementation of some commands:

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();
    }
}

Registering commands at application start:

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

Executing the command when received:

_commands(commandText).ExecuteCommand(e.channel);
like image 42
Bradley Uffner Avatar answered Oct 12 '22 23:10

Bradley Uffner