I'm trying to dynamically call a method that accepts either a string or null. Im trying to build a command line program and I would like the commands to be able to accept no or some number of arguments. Here's the code to call the methods :
public class Command {
public static void Interpret(AST.Command Command) {
if (Command.command != null) {
typeof(Interpreter.Command).GetMethod(Command.command)?.Invoke(null, Command.args.ToArray());
}
}
public static void quit(string? args) {
System.Environment.Exit(1);
}
public static void info(string? args) {
Console.WriteLine("MathDoer Info :\n\tVersion : 0.0.1\n\tBy : Wiazarr");
}
}
It is my understanding that the question mark after the string in the method declaration allows the string to be null and thus making the variable null, and it works if I call the methods normally but not while calling it with the Invoke method. How can I fix this?
When calling by reflection, pass null or use Type.Missing to indicate a default argument. The argument count still has to match.
Here's some bare bones code I used to reproduce your issue. Does this help?
internal class Program
{
static void Main(string[] args)
{
Console.Title = "Invoke with Reflection";
var method = typeof(Command).GetMethod("info");
method?.Invoke(null, new object[] { "NOT NULL" });
method?.Invoke(null, new object[] { null });
Console.ReadKey();
}
}
Mock minimal
public class Command
{
public static void info(string? args)
{
if(string.IsNullOrWhiteSpace(args))
{
Console.WriteLine("Called null or empty");
}
else
{
Console.WriteLine($"Called with {(string)args}");
}
}
}
Default Value in C#
After reading your comment, I wanted to mention that ? the context of an argument means that a null value is allowed. This is different from having a default value.
This is easier to show with an int argument which ordinarily isn't nullable.
internal class Program
{
static void Main(string[] args)
{
var method = typeof(Command).GetMethod("methodWithInt");
// Without the '?' this will not compile
Command.methodWithInt(null);
method?.Invoke(null, new object[] { null });
method?.Invoke(null, new object[] { 1 });
// Without the '= 10' default value
// this throws a runtime exception
method?.Invoke(null, new object[] { Type.Missing });
}
}
public class Command
{
public static void methodWithInt(int? args = 10)
{
if(Equals(args, null))
{
Console.WriteLine("Called null.");
}
else
{
Console.WriteLine($"Called with {args}");
}
}
}
I am building a logging class and it formats the message differently based on the method name. I want to be able to use it in a similar way to how Console.WriteLine() works. Overloading works just fine, but I want to do this with a set of different methods, the name being the determining factor on the formatting option.
I am looking to see if there is a 'smarter' way to do this other then writing every overloaded method of every version of the same method over and over. That just feels so, bug prone.
Some examples of how I want to use it:
Logger Log = new Logger();
Log.Prod ("Main Log Window");
Log.Warn ("This is a Warning Message");
Log.Error ("This is an Error Message");
Log.Network ("This is a Network Message");
Log.Verbose ("This is for Verbose Message Reporting");
Log.Processing ("Analaysis Related Message");
For what I mean how to use Console.WriteLine, I mean in ways like this:
String Name = "Sam";
String Day = "Monday";
int hour = 11;
int minute = 30;
Console.WriteLine ("Hello World");
Console.WriteLine ("Hello {0}", Name);
Console.WriteLine ("{0}, the time is {1}:{2}", Name, hour, minute);
So, what I would end up with is more like this:
Logger Log = new Logger();
Log.Prod ("Staring Application");
Log.Warn ("Data {0} looks Corrupt",data);
Log.Error ("Error: {0}", message);
Log.Network ("User {0} logged in from {1}", user, network);
Log.Verbose ("This is for Verbose Message Reporting");
Log.Processing ("Analaysis Related Message");
Now, I know I can code each and every overloaded method for each of those versions of the same routine, (remember, each will format the message a little differently), and I could put some kind of 'code' on it, like:
Log.Write (Log.Error ,"Error: {0}", message);
... and Log.Error be some kind of enum or what ever, but this is nasty.
What I'd like to do is bake this all down to a case statement that would handle the formatting and then feed that into the output. Most of the code I need to write is all this overloading relaying that's 99% repeat of the same exact thing. There's got to be a smarter way.
I've seen a few things in other languages that might do this, but I am looking for a C# (.Net Framework 4.8) way of doing this if possible. What would be the 'cleanest' and 'smarter' way of doing it besides writing it all out?
I've looked at overloading, overriding, templates, interfaces, etc. But so far, it all looks as if it would wind up being the same outcome or worse.
-- edit
I have an old version that did something like this:
...
public static void Log (Type LogType, object Msg, object [] args ) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), args);
Logger.Log ((int) LogType, NewMsg);
}
public static void Log (Type LogType, object Msg, object arg1, object arg2 ) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), arg1, arg2);
Logger.Log ((int) LogType, NewMsg);
}
public static void Log (Type LogType, object Msg, object arg1, object arg2, object arg3 ) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), arg1, arg2, arg3);
Logger.Log ((int) LogType, NewMsg);
}
...
What I was trying to see is if I could get rid of that 'Type' parameter and use the name of the function to forward it. Maybe that is some kind of template or overloading. I think I might have built it doing some copy/paste from Console.WriteLine, (been a while) or at least used it as a starting place.
I know the long way works, I was just hoping for a 'smarter' version, but so far, it doesn't seem there is one.
This likely has errors, just tossed it together as an example of the long way...
...
public static void Warn(object Msg, object[] args) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), args);
Logger.Log(1, NewMsg);
}
public static void Warn(object Msg, object arg1, object arg2) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), arg1, arg2);
Logger.Log(1, NewMsg);
}
public static void Warn(object Msg, object arg1, object arg2, object arg3) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), arg1, arg2, arg3);
Logger.Log(1, NewMsg);
}
...
public static void Error(object Msg, object[] args) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), args);
Logger.Log(2, NewMsg);
}
public static void Error(object Msg, object arg1, object arg2) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), arg1, arg2);
Logger.Log(2, NewMsg);
}
public static void Error(object Msg, object arg1, object arg2, object arg3) {
// Using Type specifier and format options.
String NewMsg = String.Format (Msg.ToString(), arg1, arg2, arg3);
Logger.Log(2, NewMsg);
}
...
So, that can get pretty long if I do what I want, and I can, I was just wondering if there was a better way.
This was when I was learning about overloading, so, yea.
--- Edit: Working:
I had to make a few tricks to get it all to gyrate, but incase anyone ever needs it, here is what I came up with:
working sample code snippets:
Logger Log = new Logger();
String name = "George";
long records = 200000;
String network = "someplace.com";
int hour = 10;
int minute = 30;
Log.Prod ();
Log.Warn ("Project Starting");
Log.Warn (network);
Log.Prod ("Hello {0}!", name);
Log.Prod ($"Hello {name}!");
Log.Processing("{0} Processed", records);
Log.Processing($"{records} Processed");
Log.Network($"{name} logged in from {network} at {hour}:{minute}");
Log.Network("{0} logged in from {1} at {2}:{3}", name, network, hour, minute);
Now, I don't have my format stuff in here yet, (not relevant but the value is there to be used, It's going to a console log window so they will be colored and various stamps will wrap them, but that's the easy part for later.)
public class Logger {
public Logger () {
if (LogSubsystem.Init == true) {
return;
} else {
// Do some setup stuff here.
}
}
public void Prod(params object[] args) {
LogMessage(1, args);
}
public void StatusBar(params object[] args) {
LogMessage(2, args);
}
public void TitleBar(params object[] args) {
LogMessage(3, args);
}
public void Verbose(params object[] args) {
LogMessage(4, args);
}
public void Status(params object[] args) {
LogMessage(5, args);
}
public void Network(params object[] args) {
LogMessage(6, args);
}
public void Account(params object[] args) {
LogMessage(7, args);
}
public void Error(params object[] args) {
LogMessage(8, args);
}
public void Warn(params object[] args) {
LogMessage(9, args);
}
public void Processing(params object[] args) {
LogMessage(10, args);
}
public void DebugLow(params object[] args) {
LogMessage(11, args);
}
public void DebugMid(params object[] args) {
LogMessage(12, args);
}
public void DebugHigh(params object[] args) {
LogMessage (13, args);
}
private void LogMessage(int mode, object [] args) {
var property = typeof(ICollection).GetProperty("Count");
int count = (int)property.GetValue(args, null);
... many things missing....
if (count == 0) {
Console.WriteLine ("");
}
if (count == 1) {
Console.WriteLine ("{0}", args[0]);
}
if (count > 1) {
ArraySegment<object> ASargs = new ArraySegment<object>(args, 1, (count-1));
object [] SubArgs = ArraySegmentToArray(ASargs);
Console.WriteLine (args[0].ToString(), SubArgs);
}
}
private object [] ArraySegmentToArray (ArraySegment<object> segment) {
var result = new object[segment.Count];
for (int i = 0; i < segment.Count; i++) {
result[i] = segment.Array[i + segment.Offset];
}
return result;
}
} // Logger class
And the output works:
Project Starting
someplace.com
Hello George!
Hello George!
200000 Processed
200000 Processed
George logged in from someplace.com at 10:30
George logged in from someplace.com at 10:30
Some of these will eventually set the status bar, title bar, etc, is why those are there. (you'd set a reference to those objects in stuff that doesn't exist here yet, my old one does it, so, moving that over) The tricky part is the ArraySegment Stuff, maybe an easier way to do it, but it's short enough and works that I don't care. It surprised to find that ArraySegment doesn't have a ToArray(), yea, didn't see that one coming. Anyway, Thanks!!!
params should do the trick for you. You don't need multiple overrides for the same name. Just one would do.
public static void Warn(string msg, params object[] args) {
Logger.Log(1, String.Format(msg, args));
}
public static void Error(string msg, params object[] args) {
Logger.Log(2, String.Format(msg, args));
}
and so on.
public void Test ()
{
string myString = "hello";
}
public void Method (string text)
{
COnsole.WriteLine ( ... + " : " + text ); // should print "myString : hello"
}
Is it possible to get name of variable passed to a method?
I want to achieve this:
Ensure.NotNull (instnce); // should throw: throw new ArgumentNullException ("instance");
Is it possible? WIth caller info or something similiar?
Without nameof operator?
Problem with reflection is that, once the C# code is compiled, it will no longer have the variable names. The only way to do this is to promote the variable to a closure, however, you wouldn't be able to call it from a function like you are doing because it would output the new variable name in your function. This would work:
using System;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
string myString = "My Hello string variable";
// Prints "myString : My Hello String Variable
Console.WriteLine("{0} : {1}", GetVariableName(() => myString), myString);
}
public static string GetVariableName<T>(Expression<Func<T>> expr)
{
var body = (MemberExpression)expr.Body;
return body.Member.Name;
}
}
This would NOT work though:
using System;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
string myString = "test";
// This will print "myVar : test"
Method(myString);
}
public static void Method(string myVar)
{
Console.WriteLine("{0} : {1}", GetVariableName(() => myVar), myVar);
}
public static string GetVariableName<T>(Expression<Func<T>> expr)
{
var body = (MemberExpression)expr.Body;
return body.Member.Name;
}
}
I want to invoke the method by the method name stored in the list. Can anyone help? I'm new to c#!
{
delegate string ConvertsIntToString(int i);
}
class Program
{
public static List<String> states = new List<string>() { "dfd","HiThere"};
static void Main(string[] args)
{
ConvertsIntToString someMethod = new ConvertsIntToString(states[1]);
string message = someMethod(5);
Console.WriteLine(message);
Console.ReadKey();
}
private static string HiThere(int i)
{
return "Hi there! #" + (i * 100);
}
}
It looks like you don't need Delegate.DynamicInvoke at all - you're not trying to invoke it dynamically - you're trying to create the delegate dynamically, which you can do with Delegate.CreateDelegate. Short but complete program based around your example (but without using a list - there's no need for that here):
using System;
using System.Reflection;
delegate string ConvertsIntToString(int i);
class Program
{
static void Main(string[] args)
{
// Obviously this can come from elsewhere
string name = "HiThere";
var method = typeof(Program).GetMethod(name,
BindingFlags.Static |
BindingFlags.NonPublic);
var del = (ConvertsIntToString) Delegate.CreateDelegate
(typeof(ConvertsIntToString), method);
string result = del(5);
Console.WriteLine(result);
}
private static string HiThere(int i)
{
return "Hi there! #" + (i * 100);
}
}
Obviously you need to adjust it if the method you want is in a different type, or is an instance method, or is public.
Not sure if this is possible but im wondering how I catch the return of two methods that are assigned to the same delegate (multicast). I basically wondered if there is a way to catch each return value? Perhaps I have to iterate over it, not really sure..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MutiCastDelegate2
{
class Program
{
delegate string HelloWorldDelegate();
static void Main(string[] args)
{
HelloWorldDelegate myDel1 = ReturnHelloWorld;
HelloWorldDelegate myDel2 = ReturnHelloWorld2;
HelloWorldDelegate myMultiDelegate = myDel1 + myDel2;
Console.WriteLine(myMultiDelegate());
Console.ReadLine();
}
public static string ReturnHelloWorld()
{
return "Return Hello World";
}
public static string ReturnHelloWorld2()
{
return "Return Hello World 2";
}
}
}
You can use MulticastDelegate.GetInvocationList() to get access to each delegate in the list, then you just have to call each one and retrieve the results:
var delegates = myMultiDelegate.GetInvocationList();
foreach (var d in delegates)
{
string result = (string) d.DynamicInvoke();
}