Reputation: 53
Good day, everyone,
recently I've come across the Discord.NET Api and was in love with the way Commands were handled. Essentially, to add a new Command that is executed when you write !ping
, you can do this:
[Command("ping")]
public async Task Ping()
{
//some logic here
}
And I really liked the easy nature of integrating new commands in an already existing API. So I wanted to recreate what was going on. In the beginning I was absolutely confused by the introduction of metaprogramming, but now feel a little more comfortable, so I tried to start, and designed an Attribute that was only assignable to methods:
[AttributeUsage(AttributeTargets.Method)]
public class Command : Attribute
{
public string Name { get; set; }
public Command(string name)
{
Name = name;
}
public Command()
{
Name = string.Empty;
}
}
Basic idea then is, that when my Console gets a command, I can run a method that has the command attribute and the name of what was entered in the console. So when I enter "ping" in the console, the below method is going to be executed.
[Command("ping")]
public void Ping()
{
//do sth
}
Now to the complicated part. How do I find and especially run that method? That's where I'm stuck right now. I really don't find anything helpful about that question on the .Net documentary or here on stackoverflow. Here is my attempt anyway:
public void Handle(string command)
{
var methods = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.GetCustomAttributes<Command>().Count() > 0
select t;
//run method where command.name = ping
}
The idea behind that being, to iterate through all available methods in the assembly, and then putting those into a List of some kind and then executing the method that has the command.name of what was passed in as an argument to the Handle function. When I get that to work, I of course will initilaize the methods list in the constructor of that class and not everytime call it when Handle is called, but for simplicity in my question I formulated my question independent of that, to have my example minimal. The question now is, how do I iterate through all methods in my assembly, and save those with the command attribute into a collection, and how do I run a method that has a certain value for the command.Name property? I'am kind of new to that whole reflection stuff, so if I did something else stupid or you have general tips on that topic please let me know!! Thanks in advance!!
Upvotes: 2
Views: 974
Reputation: 501
I have written a small demo application that should help you to complete your logic. Overall, of course, it still has room for improvement, but it works:
using System;
using System.Linq;
using System.Reflection;
namespace DemoApp
{
class Program
{
static void Main(string[] args)
{
string command = Console.ReadLine().Trim();
LogicProvider provider = new LogicProvider();
MethodInfo method = provider.GetType().GetMethods().FirstOrDefault((item) => item.GetCustomAttribute<CommandAttribute>().Identifier == command);
method?.Invoke(provider, null);
}
}
public class LogicProvider
{
[Command("DemoCommand")]
public void MyMethod()
{
Console.WriteLine("Here");
}
}
public class CommandAttribute : Attribute
{
public CommandAttribute(string identifier)
{
this.Identifier = identifier;
}
public string Identifier { get; } = null;
}
}
If DemoCommand is entered in the console, then a search is made in the LogicProvider for a matching method. If there is a match, it is executed.
The whole thing also works with methods that have parameters. In the case of method?.Invoke(), this can then be specified.
Upvotes: 3