I'm making a command system for my console app. Some commands are used "as-is" (like "exitapp"), some require arguments (like "server start ip port"). Here's how I am doing it:
delegate void CommandHandlerMethod(params object[] args);
readonly Dictionary<string, CommandHandlerMethod> commands;
For methods which doesn't require arguments, i use discarding feature like this:
void ExitAppCommand(params object[] _) { ... }
void StartServerCommand(params object[] args)
{
// ip = args[0];
// port = args[1];
}
Then I just loop through dictionary, and I want to decide between two situations:
1. if command-handling method discard arguments then just call it
2. otherwise cut arguments from input string and pass it as args
So what is the option to detect argument discard?
UDP: This is "pseudo" of what I exactly want:
foreach (KeyValuePair<string, CommandHandlerMethod> cmd in commands)
{
(input.StartsWith(cmd.Key))
{
if (cmd.Value. /*is declared to discard arg */)
cmd.Value();
else
// else cut args and pass them
cmd.Value(input.Substring(cmd.Key.Length+1).Split(' '));
break;
}
}
The _ in void ExitAppCommand(params object[] _) { ... } is not a discard. _ is a valid C# identifier.
In the following code, however, _ is a discard:
public void N(int x)
{
_ = x.ToString();
_ = 2 + 3;
}
Chexk it out here.
Related
I'm looking for a way to get hold of the name of a variable that was passed into an extensionmethod. I want to have the name of the parameter in the calling variable. Sounds strange, let me explain.
assume this piece of testing code
private static void TestingMethod(string firstParam, int secondParam, bool thirdParam)
{
try
{
throw new InvalidOperationException("This named optional params stuff, it's just not working boss");
}
catch (Exception ex)
{
GenericLog_Exception(ex, "it's on fire, please help");
}
}
On the last line you can see the exception being logged. I want to be able to provide optional parameter support. So the developers can add parameter information when needed.
I've seen many posts about this on stackoverflow, lot's of different approaches. Main thing is; it can't be done fully generically.
Some code for explanation:
static string GetCallingMethodSignature()
{
StackTrace stackTrace = new StackTrace();
// Get calling method name
var callingMethod = stackTrace.GetFrame(1).GetMethod();
var callingMethod_Name = callingMethod.Name;
// get calling method params
string retVal = string.Empty;
var callingMethod_Parameters = callingMethod.GetParameters();
retVal = callingMethod_Name + "(";
foreach (var param in callingMethod_Parameters)
{
retVal += param.Name + ": " + param.ToString() + ",";
}
retVal.Remove(retVal.Length - 1, 1);
retVal += ")";
return retVal;
}
Now, this testing code is getting the calling method name and it's parameters. That is, the names of the parameters. But not their values. The param.tostring() part only returns the type name. Not the value. I've been reading on this and it seems this can't be done via reflection.
So then I went for another approach, why not provide the parameters the developer finds suitable for logging. You don't need all of them most of the time anyway.
private static string GenericLog_Exception(Exception exceptionData, string extraInformation, params KeyValuePair<string, object>[] parameters)
So, this being a new testmethod, i'm providing the parameters of choice into the exception logging method. But if you want to make this work, it's one hell of a job everytime to make this call.
private static void TestingMethod(string firstParam, int secondParam, bool thirdParam)
{
try
{
throw new InvalidOperationException("This named optional params stuff, it's just not working boss");
}
catch (Exception ex)
{
GenericLog_Exception(ex, "it's on fire, please help", new KeyValuePair<string, object>[]{
new KeyValuePair<string, object>("firstParam", firstParam),
new KeyValuePair<string, object>("secondParam", secondParam),
new KeyValuePair<string, object>("thirdParam", thirdParam)
});
}
}
Now this is working. But as said, i find the bottom part to cumbersome. I was thinking along the lines of an extensionmethod, so I can shorten the creation of each kvp.
internal static class ExtensionMethodsForTesting
{
internal static KeyValuePair<string, object> AsKeyValuePair(this string parameter)
{
var name = nameof(parameter);
return new KeyValuePair<string, object>(name, parameter);
}
}
And this would then be used as
GenericLog_Exception(ex, "it's on fire, please help", new KeyValuePair<string, object>[] { firstParam.AsKeyValuePair() });
This confronts me with the same issue I had before; the nameof(parameter), ofcourse, returns "parameter". I would also have to make a couple of extension methods for each type. Or check for the type in the extension method to make sure i get the correct value.
So, in short: how can i get the name of this variable that invokes the extension method?
You could do the following "hack". Change the signature of your method to the following:
private static string GenericLog_Exception(
Exception exceptionData,
string extraInformation,
params Expression<Func<object>>[] parameters)
And now your call site would look a little bit cleaner:
GenericLog_Exception(ex,
"it's on fire, please help",
() => firstParam,
() => secondParam,
() => thirdParam);
And you can extract parameter info from the expressions the following way (using C# tuple support for convenience):
private static (object Value, string ParamName) GetParameterInfo
(Expression<Func<object>> expr)
{
//First try to get a member expression directly.
//If it fails we know there is a type conversion: parameter is not an object
//(or we have an invalid lambda that will make us crash)
//Get the "to object" conversion unary expression operand and then
//get the member expression of that and we're set.
var m = (expr.Body as MemberExpression) ??
(expr.Body as UnaryExpression).Operand as MemberExpression;
return (expr.Compile().Invoke(), m.Member.Name);
}
I was trying to create a general function, that should describe some threads to the windows event log to different internal methods. So i wrote:
void SubScribeToEventLogEvent(string EventLog,int EventID,string Source, Action named ="myMethod")
{
string query = "*[System/Level=8] and [System/EventID=" +EventID.ToString()+"]";
EventLogQuery subscriptionQuery = new EventLogQuery(EventLog, PathType.LogName, "*[System/Level=8]");
EventLogWatcher watcher = new EventLogWatcher(subscriptionQuery);
watcher.EventRecordWritten += new EventHandler<EventRecordWrittenEventArgs>(named);
watcher.Enabled = true;
}
Then I have a few functions triggered for specific events
private void myMethod(object obj,EventRecordWrittenEventArgs arg)
{
//do something
if (arg.EventRecord != null)
{
MessageBox.Show(arg.EventRecord.Id.ToString() + arg.EventRecord.FormatDescription());
Console.WriteLine("Received event {0} from the subscription.", arg.EventRecord.Id);
Console.WriteLine("Description: {0}", arg.EventRecord.FormatDescription());
}
else
{
Console.WriteLine("The event instance was null.");
}
}
So that in my main I could simply write
SubScribeToEventLogEvent(EventlogName,200,myMethod);
However it doesn't seam to work, I am coding in .net 4.0 and I believed that Action is a reserved method word for doing such things. But I get an error (red line already under named in the first line of the code):
SubScribeToEventLogEvent(string EventLog,int EventID,string Source, Action named ="myMethod")
apparently it cann't be assigned a string value (but what else?), and if I remove the string value its still not recognized in the Eventhandler function.
I wrote a Discord Bot. It's developed with C#. My command list is filled, the command value receives this list. But the command does not execute the code when calling it.
My prefix char is '!' followed by the command. My base class looks this:
public class Bot
{
string token = "#######################"; // my Token
CommandService command; // The commands holder
EventController eventController = new EventController(); // event class
CommandController commandController = new CommandController(); // commands class
public Bot()
{
var client = new DiscordClient(); // my client
client = new DiscordClient(input =>
{
input.LogLevel = LogSeverity.Info;
input.LogHandler = Log;
});
client.UsingCommands(input =>
{
input.PrefixChar = '!'; // the prefix char to call commands
input.AllowMentionPrefix = true;
});
eventController.HandleEvents(client); // reference to events
command = client.GetService<CommandService>();
commandController.HandleCommands(command, client); // reference to commands
client.ExecuteAndWait(async() =>
{
while (true)
{
await client.Connect(token, TokenType.Bot);
break;
}
});
}
private void Log(object sender, LogMessageEventArgs e)
{
Console.WriteLine(e.Message);
}
I devided the code into two classes, the eventController and the commandController. The commandController is the relevant one.
My command class looks this:
private List<Tuple<string, string, string>> commandList = new List<Tuple<string, string, string>>(); // the List holding all commands
public void HandleCommands(CommandService command, DiscordClient client)
{
FillCommandList(); // Fill the List with the commands
foreach (Tuple<string, string, string> tuple in commandList)
{
command.CreateCommand('!' + tuple.Item1).Do(async (e) =>
{
await e.Channel.SendMessage(tuple.Item2); // Create all commands from the List
});
}
}
private void Add(string commandName, string textToReturn, string commandDescription)
{
commandList.Add(new Tuple<string, string, string>(commandName, textToReturn, commandDescription)); // Method to lower the mass of code
}
private void FillCommandList()
{
Add("test0", "success0", "info0"); // commands for testing
Add("test1", "success1", "info1");
Add("test2", "success2", "info2");
Add("test3", "success3", "info3");
Add("help", UseHelp(), "List all Commands"); // call the help
}
private string UseHelp()
{
string commandItems = "";
foreach (Tuple<string, string, string> tuple in commandList)
{
commandItems += "- !" + tuple.Item1 + " - " + tuple.Item3 + "\r\n"; // List all commands
}
return commandItems;
}
So when I call a command like "test0" or "UseHelp()" the command receives the string content. All 5 commands are listed to the bot. But when i use a command in Discord the Bot does not reply.
It is connected and the "command" data is filled...
First, take a look at this :
client.UsingCommands(input =>
{
input.PrefixChar = '!'; // the prefix char to call commands
input.AllowMentionPrefix = true;
});
and now , this :
command.CreateCommand('!' + tuple.Item1)
In discord.net , when you make a PrefixChar already, the PrefixChar will always appear inside the argument of command.CreateCommand() at the front by default. So hence there is no need to place another '!' inside. If you do that, you have to call a command by using !!test0 . Simply, treat it as the system has automatically added the prefix in the argument on command.CreateCommand() automatically at the front.
To fix it : simply remove char argument '!' at the front in command.CreateCommand('!' + tuple.Item1). Test the bot by calling !test0 or something, it should work.
I've created a generic delegate and I want to assign to it a function without any arguments.
Is it possible?
Here is what I tried so far:
class Program
{
public delegate void TemplateDel<T>(T item);
public static void fWriteLetters(char[] p_cLetters)
{
for (int i = 0; i < p_cLetters.Length; i++)
Console.WriteLine(p_cLetters[i]);
}
void fNoArg()
{
Console.WriteLine("No arguments!");
}
static void Main(string[] args)
{
TemplateDel<char[]> l_dWriteLeters = new TemplateDel<char[]>(fWriteLetters);
TemplateDel<void> l_dNoArg = new TemplateDel<void>(fWriteLetters);
}
}
the last line of code doesn't compile...
No it is not possible.void is only valid in return types.You can't use it as a type in other contexts. (except the unsafe context)
You need add another overload for your delegate.
public delegate void TemplateDel();
Or simply use an Action.
As Selman22 notes in the other answer:
No it is not possible.
But there is another way, use a lambda to throw away the argument:
TemplateDel<bool> l_dNoArg = new TemplateDel<bool>(_ => fWriteLetters);
(Using _ as the identifier here matches F#'s wildcard – ignore this argument – syntax.)
While not helpful here, this kind of wrapper is helpful when arguments are not interested in are passed and saves writing an extra member just to ignore or re-arrange arguments.
So I want other users to be able to run my programm sending arguments. how to do such thing?
If you have a Main method (which you'll have with a command-line app) you can access them directly as the args string-array parameter.
public static void Main(string[] args) {
var arg1 = args[0];
var arg2 = args[1];
}
If you're some other place in your code you can access the static Environment.GetCommandLineArgs method
//somewhere in your code
var args = Environment.GetCommandLineArgs();
var arg1 = args[0];
var arg2 = args[1];
You mean args when launching? such as myapp.exe blah blah2 blah3
Make your main method look like this:
public static void Main(string[] args)
{
}
now args is an array of the arguments passed into the program. So in the example case, args[0] == "blah", args[1] == "blah2", etc
The program is run from a method with this signature
public static void Main(string[] args)
The parameter args will contain the command line arguments, split on space.
While string[] args works just fine, it's worth mentioning Environment.GetCommandLineArgs.
You can read command line arguments from Main's optional string[] parameter:
static void Main(string[] args)
{
if (args.Length >= 1)
{
string x = args[0];
// etc...
}
}
Note that the following declaration for the Main method is also valid, but then you don't have access to the command line arguments:
static void Main()
{
// ...
}
See the documentation for more details.
This is supported by default, and the arguments will appear in the args array passed to your program.
public static void Main(string[] args)
If you say
App.exe Hello World What's Up
On a command line, you will receive an args array like this:
[0] = "Hello"
[1] = "World"
[2] = "What's"
[3] = "Up"
It's just up to you to determine what arguments you want, how they will be formatted, etc.
try these:
http://sourceforge.net/projects/csharpoptparse/
http://www.codeproject.com/KB/recipes/command_line.aspx
they basically allow you to define args and parse them in an OO way rather than having to lots of string comparisons and stuff like that. i used a similar one for java and it was great