0

I am making an app which is interfaced with at the command line by typing commands that then call corresponding methods. The thing is, some of these methods are asynchronous, and thus, according to what I've heard, should return Task instead of void, even when their return value is not used (async is not necessary for the program I am making, however a library I am using in some of the methods is asynchronous).

Because of this, I can't use a dictionary of delegates (as far as I know), as they would be different types, so I have tried using reflection.

MethodInfo command = MethodBase.GetCurrentMethod()
    .DeclaringType
    .GetMethod(_commands[args[0]]);
command.Invoke(null, new string[][] { args });

The above snippet is intended to get a static method by its name and then call it with argument string[] args.

According to the documentation I'm looking at, alongside other StackOverflow answers, the first argument should be null if the method being called is static, however I get a NullReferenceException anyway. Why is this, and how do I fix it?

nvoigt
  • 75,013
  • 26
  • 93
  • 142

2 Answers2

0

You must check that the command is not null.

If you don't want to handle the case to only call it if not null, you can simply write:

command?.Invoke(null, new string[] { args });

Thus if the method does not exist, GetCurrentMethod returns null and nothing is done.

But if you want to manage the case you need to use a test and for example show a system message.

You should also hardness the code by checking if args is noty empty too.

And you should also add some bindings flags to the search.

if (args.Length == 0)
{
  Console.WriteLine("No command provided.");
  return;
}

string commandName = _commands[args[0]];

// You can remove non public or public depending on the nature of your methods
var flags = var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;

var command = MethodBase.GetCurrentMethod().DeclaringType.GetMethod(commandName, flags);

if (command == null)
{
  Console.WriteLine("Command not found: " + commandName);
  Console.WriteLine("Allowed commands are:"
  Console.WriteLine("- ...");
  Console.WriteLine("- ...");
  return;
}

command.Invoke(null, new string[] { args });

I removed the jagged array [][] as seen and suggested by @DmitryBychenko in case of it was a mistake (it should be) that causes the error in the executed method if it exists.

Here a some example of advanced command line arguments parsing:

Parsing command-line options in C#

Best way to parse command line arguments in C#?

https://codereview.stackexchange.com/questions/369/parsing-console-application-arguments

https://github.com/mykeels/CommandLineParser

https://github.com/commandlineparser/commandline

https://www.codeproject.com/Articles/19869/Powerful-and-simple-command-line-parsing-in-C

0

Well, GetMethod can well return null or some non static method

  MethodInfo command = MethodBase
    .GetCurrentMethod()
    .DeclaringType
    .GetMethod(_commands[args[0]]);

So we have to check if command is valid one; since args[0] is used in finding the method, I guess it should be removed from parameters (Skip(1)):

  if (command != null && command.IsStatic)
    command.Invoke(null, args.Skip(1).Cast<Object>().ToArray());

please, note, that you have to do more validation if method can be overload (i.e. we have several methods with the same name), something like this:

  MethodInfo command = MethodBase
    .GetCurrentMethod()
    .DeclaringType
    .GetMethods(BindingFlags.Public | BindingFlags.Static) 
    .Where(m => m.Name == _commands[args[0]])
    .Where(m => m.GetParameters().Length == args.Length - 1) // - 1 for Skip(1)
    .FirstOrDefault(); 
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215