Delegate.DynamicInvoke - c#

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.

Related

Why isnt function executed?

I am working on creating an app just like the Output Terminal (Unix) or the Command Line Prompt (Windows). I have created a dictionary that has some keywords for a function. But when I call those keywords, nothing is done. My program is called Command Line Control (or CLC). And I use the .NET Core which is for all running software(Linux, macOS, and Windows).
I do not know what to try exactly. A function is a function. It must be executed when I call it.
using System;
using System.Collections.Generic;
using System.Threading;
using System.IO;
namespace CLC
{
class Program
{
static DirectoryInfo maindirectory;
static Dictionary<string, string> keyaction;
static string value;
static void WritePathOfWorkingDirectory(DirectoryInfo directory)
{
if (directory != null)
{
Console.Write("{0}:", directory.FullName);
}
else
{
}
}
static void ProcessAnswer(string[] array)
{
string action = array.GetValue(0).ToString();
value = array.GetValue(1).ToString();
string c = keyaction[action];
Console.Write(c);
}
static string ListFiles()
{
foreach(var file in maindirectory.GetFiles())
{
Console.WriteLine(file.Name);
}
return "ok";
}
static string ListDirectories()
{
foreach(var directory in maindirectory.GetDirectories())
{
Console.WriteLine(directory);
}
return "ok";
}
static void MainProgramm()
{
WritePathOfWorkingDirectory(maindirectory);
string data = Console.ReadLine();
var arry = data.Split(' ');
ProcessAnswer(arry);
Thread repeat = new Thread(MainProgramm);
repeat.Start();
}
static void Main(string[] args)
{
maindirectory = new DirectoryInfo("C:/Users");
keyaction = new Dictionary<string, string>();
keyaction.Add("lf", ListFiles());
keyaction.Add("ld", ListDirectories());
Console.Clear();
maindirectory = new DirectoryInfo("C:/Users");
Thread thread = new Thread(new ThreadStart(MainProgramm));
thread.Start();
}
}
}
The expected result is to do what the key says: for example if I type ld (list directories) the list directory function should be executed. But I don't get anything! The program just repeats itself.
When you include the parentheses after a Method name, you invoke the method. So rather than passing the function as the Dictionary value, you're passing the result of calling that function. You need to get a reference to the function to be called then invoke it.
Your Main method should look like this:
static void Main(string[] args)
{
maindirectory = new DirectoryInfo("C:/Users");
keyaction = new Dictionary<string, Func<string>>(); // changed this to an Func instead of a string
keyaction.Add("lf", ListFiles); // notice I removed the parentheses here
keyaction.Add("ld", ListDirectories); // and here
Console.Clear();
maindirectory = new DirectoryInfo("C:/Users");
Thread thread = new Thread(new ThreadStart(MainProgramm));
thread.Start();
}
and keyaction should be declared like this:
static Dictionary<string, Func<string>> keyaction; // a Func<string> is a function that returns a string and takes no arguments
Then, in your ProcessAnswer method you need to call the function via the reference you have to it in the Dictionary:
static void ProcessAnswer(string[] array)
{
string action = array.GetValue(0).ToString();
value = array.GetValue(1).ToString();
string c = keyaction[action](); // calling the referenced funtion
Console.Write(c);
}
This should give you the expected result.
In the current state, your program's methods only get called once and then you clear the Console before you can see the output, so it probably looks like your program just says "ok" whenever you enter a command.
The main problem is that you're storing a string value in the dictionary rather than the function itself, so when you add the functions, you're adding their immediate return value (before you ever prompt the user for input).
Instead, the dictionary value should be a Func<string>, which you can then .Invoke on demand.
Additionally, it seemed that you weren't validating the user entry. We can use TryGetValue for the user's entry to ensure that they entered a valid command (if it exists in the dictionary). If it doesn't, we can tell them their input was invalid.
Also, when accessing array values, you should first ensure that the index you're accessing is valid (if the user only enters one command, like "lf"with no value, then you can't access array index [1] because it doesn't exist.
Also, it seems like even when the user does input a path, you store it in value, but never use it. So I modified the ListFiles and ListDirectories methods to treat the value as a path, and if it exists, list the files or directories from the path specified.
Try out this code with the changes:
public class Program
{
private static DirectoryInfo _maindirectory = new DirectoryInfo("C:\\Users");
private static Dictionary<string, Func<string>> _keyaction =
new Dictionary<string, Func<string>>
{
{"lf", ListFiles},
{"ld", ListDirectories},
{"cd", ChangeDirectory},
{"exit", Exit}
};
private static string _value;
private static void DisplayPrompt(FileSystemInfo directory)
{
Console.Write($"{directory?.FullName ?? "[cmd]"}: ");
}
private static void ProcessAnswer(IReadOnlyList<string> array)
{
var action = array.Count > 0 ? array[0] : string.Empty;
_value = array.Count > 1 ? array[1] : null;
Func<string> method;
_keyaction.TryGetValue(action, out method);
if (method == null)
{
WriteError($"Unknown command: {action}");
}
else
{
Console.WriteLine(_keyaction[action].Invoke());
}
}
private static string ListFiles()
{
var dir = Directory.Exists(_value) ? new DirectoryInfo(_value) : _maindirectory;
foreach (var file in dir.GetFiles())
{
Console.WriteLine(file.Name);
}
return "ok";
}
private static string ListDirectories()
{
var dir = Directory.Exists(_value) ? new DirectoryInfo(_value) : _maindirectory;
foreach (var directory in dir.GetDirectories())
{
Console.WriteLine(directory);
}
return "ok";
}
private static string ChangeDirectory()
{
if (Directory.Exists(_value))
{
_maindirectory = new DirectoryInfo(_value);
}
else if (Directory.Exists(Path.Combine(_maindirectory.FullName, _value)))
{
_maindirectory = new DirectoryInfo(
Path.Combine(_maindirectory.FullName, _value));
}
else
{
WriteError("Directory not found.");
}
return "ok";
}
private static void WriteError(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ResetColor();
}
private static string Exit()
{
Environment.Exit(0);
return "ok";
}
private static void Main()
{
while (true)
{
DisplayPrompt(_maindirectory);
ProcessAnswer(Console.ReadLine()?.Split(' '));
}
}
}

Declaring an object of C# struct

I'm very new to C#. Coming from pure C/C++ background. Hence please go easy on me, if my question is basic.
I'm trying to declare an object of struct Abc which is complaining a compilation error as below which means that, the object obj is NOT recognized.
string mains1 = obj.s1;
error CS0120: An object reference is required for the non-static
field, method, or property 'csharp1.Program.obj'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace csharp1
{
class Program
{
public struct Abc
{
public string s1;
};
Abc obj = new Abc();;
static void Main(string[] args)
{
System.Console.WriteLine("start main");
string cwd = Directory.GetCurrentDirectory();
string fileName = "Myfile.xml";
string destFile = System.IO.Path.Combine(cwd, fileName);
string inpFile = "MyfileINP.xml";
string inpDir = System.IO.Path.Combine(cwd, "SubDir");
string srcfile = System.IO.Path.Combine(inpDir,inpFile);
File.Copy(srcfile, destFile, false);
string mains1 = obj.s1;
}
}
}
Not sure what is the error here.
This is complaining because you are trying to access a non static instance "obj" from a static context the Main method so there are multiple ways to resolve it you can make that instance static too like "static Abc obj=new Abc()" or you can shift that declaration inside Main method or you can create an instance of Program class and then use that instance.
METHOD 1:
static Abc obj = new Abc();
static void Main(string[] args)
{
System.Console.WriteLine("start main");
//
string mains1 = obj.s1;
}
METHOD 2:
static void Main(string[] args)
{
Abc obj = new Abc();
System.Console.WriteLine("start main");
//
string mains1 = obj.s1;
}
METHOD 3:
Abc obj = new Abc();
static void Main(string[] args)
{
System.Console.WriteLine("start main");
//
string mains1 = new Program().obj.s1;
}
Its always not a good practice to make "static" everywhere until
there is no other option you have, making static allow this object to
be shared across all the instances of this class so its not good thing
until we really need that.
You need to create an instance of the containing class Program before accessing it's member(s) like
string mains1 = new Program().obj.s1;
You are trying to access obj (a non-static method) from within Main (a static method). Because of this, your are getting CS0120. I advise changing Abc obj = new Abc() to static ABC obj = new Abc().

C# Pass Class as parameter to method and call static method in there

I am new to C# and looking the best way to do the following implementation.
My project got several Model classes (like 25) and each class got a static method called "process()", which just takes a bit of time to finish it's task.
I need to call all these method in each class one after another and to add some logs (write status of method execution in to file) to track execution.
I can simply do the following, But surely there should be a better professional way to do this
Log.WriteLog(DateTime.Now + " Class A Process Started");
ClassA.Process()
Log.WriteLog(DateTime.Now + " Class A Process Finished");
Log.WriteLog(DateTime.Now + " Class B Process Started");
ClassB.Process()
Log.WriteLog(DateTime.Now + " Class B Process Finished");
............ continue 25 classes
What I am trying to do is to write a method and just Add Logs and repetitive work in there..
private void CommonMethod(Class)
{
Check what class
Add Process started Log
Call Process method
Add proicess finished Log
}
You could create function that takes a delegate and performs the logging, something like this:
public void ProcessAndLog(Action action, String processName)
{
Log.WriteLog(DateTime.Now + $" Class {processName} Process Started");
action();
Log.WriteLog(DateTime.Now + $" Class {processName} Process Finished");
}
and call it like so:
ProcessAndLog(ClassA.Process, "A"); //"A" could be part of ClassA - e.g. ClassA.Name;
ProcessAndLog(ClassB.Process, "B");
//etc
This will work so long as every Process method has no params and retuns void - the signature of the Action delegate.
If it has parameters, you can call it like so:
ProcessAndLog(() => ClassC.Process("value"), "C");
If you need a return value, consider a Func<T> instead of Action.
You can do this:
private void CommonMethod<T>()
{
//Add Process started Log
//Call Process method
typeof(T).GetMethod("process")?.Invoke(null, null); // not target needed
//Add proicess finished Log
}
Usage:
CommonMethod<ClassA>();
CommonMethod<ClassB>();
Static interfaces don't exist in C#. The only way to reference a static member of a class is by its class name and member name.
An alternative would be to use reflection. Get the static method by it's string name and invoke it. Like this:
static void CommonMethod(Type type)
{
MethodInfo methodInfo = type.GetMethod("TheStaticMethodName");
if (methodInfo != null)
{
methodInfo.Invoke(null, new object[0]);
}
}
//Invoke it like this
CommonMethod(typeof(MyStaticType));
The first parameter for Invoke is the target. For an instance method you would pass a class instance you want to invoke on, but for static members just put null.
The second parameter is the arguments. You can put an empty array the if there's no arguments.
Also, you could have the same method with a generic type like this:
static void CommonMethod<T>()
{
MethodInfo methodInfo = typeof(T).GetMethod("TheStaticMethodName");
if (methodInfo != null)
{
methodInfo.Invoke(null, new object[0]);
}
}
Note that generics aren't always the best since they generate a lot of stuff at compile time.
Here's another suggestion:
class Program
{
static void Main(string[] args)
{
var x = new Calculate { a = 1, b = 2 };
var y = new Calculate { a = 10, b = 20 };
var z = new Calculate { a = 100, b = 200 };
var calculations = new List<Calculate>{
new Calculate() { a = 1, b = 2 },
new Calculate() { a = 10, b = 20 },
new Calculate() { a = 100, b = 200 }
};
calculations.ForEach(c =>
{
c.Process();
});
}
}
class Calculate
{
public int a { get; set; }
public int b { get; set; }
public void Process()
{
Console.WriteLine(a + b);
}
}

CallerInfo, get name of variable passed to method (like nameof)

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

Using delegates with arguments

I have a class 'KeyEvent'; one of which's members is:
public delegate void eventmethod(object[] args);
And the method passed to the object in the constructor is stored in this member:
private eventmethod em;
Constructor:
public KeyEvent(eventmethod D) {
em = D;
}
public KeyEvent(eventmethod D, object[] args) : this(D) {
this.args = args;
}
public KeyEvent(Keys[] keys, eventmethod D, object[] args) : this(keys, D) {
this.args = args;
}
The 'eventmethod' method is then called by using the public method "ThrowEvent":
public void ThrowEvent() {
if (!repeat && thrown) return;
em.DynamicInvoke(args);
this.thrown = true;
}
As far as I can see, this compiles fine. But when trying to create an instance of this class (KeyEvent), I'm doing something wrong. This is what I have so far:
object[] args = {new Vector2(0.0f, -200.0f)};
Keys[] keys = { Keys.W };
KeyEvent KeyEvent_W = new KeyEvent(keys, new KeyEvent.eventmethod(GameBase.ChangeSquareSpeed), args);
GameBase.ChangeSquareSpeed doesn't do anything at the moment, but looks like this:
static public void ChangeSquareSpeed(Vector2 squarespeed) {
}
Anyway, the erroneous line is this one:
KeyEvent KeyEvent_W = new KeyEvent(keys, new KeyEvent.eventmethod(GameBase.ChangeSquareSpeed), args);
The error that the compiler gives me is:
error CS0123: No overload for 'ChangeSquareSpeed' matches delegate 'BLBGameBase.KeyEvent.eventmethod'
My question is: Does this mean I have to change ChangeSquareSpeed to take no parameters (in which case, what is a better way of doing this?), or am I doing something syntactically wrong?
Thank you in advance.
I think the error is very explicit. Your ChangeSquareSpeed method doesn't match the delegate . The delegate expects a method with one object[] as parameter but your passing a method with a Vector2 as a parameter, hence the error.
Try this method:
static public void ChangeSquareSpeed(object[] squarespeed)
{}
(update)
I see some confusion in your code, specially in the line:
object[] args = {new Vector2(0.0f, -200.0f)};
I can't really understand if you want an array of Vector2's or just a Vector2's object.
If you pretend to have an array of Vector2's I think this might seem reasonable:
Change the delegate to:
public delegate void eventmethod(Vector2 args);
and then
public void ThrowEvent() {
if (!repeat && thrown) return;
foreach(object obj : args)
{
em.DynamicInvoke((Vector2)obj);
}
this.thrown = true;
}
(update 2)
In that case, I think you should create a generic version of KeyEvent. See this example and go from there:
class KeyEvent<T>
{
public T Args { get; set; }
public Action<T> A { get; set; }
public KeyEvent() { }
public void ThrowEvent()
{
A.DynamicInvoke(Args);
}
}
// ...
static void M1(object[] o)
{
Console.WriteLine("M1 {0}", o);
}
static void M2(Vector2 v)
{
Console.WriteLine("M2 {0}", v);
}
static void Main(string[] args)
{
KeyEvent<object[]> e1 = new KeyEvent<object[]>
{
A = new Action<object[]>(M1),
Args = new object[] {};
};
KeyEvent<Vector2> e2 = new KeyEvent<Vector2>
{
A = new Action<Vector2>(M2),
Args = new Vector2();
};
}
The delegate eventmethod states that all events using it should take object[] (args) as their only in parameter. Depending on what you're using this code for, you want to either:
Change the signature of ChangeSquareSpeed to ChangeSquareSpeed(object[] squarespeed)
Create a new delegate, with the signature void neweventmethod(Vector2 args); and use that
Change the signature of eventmethod to the above
If you are on C# 3, change the delegate to an Action<object[]>. That will make your life much simpler, as it will be type-safe to invoke it.
That would allow you to simply invoke it like this:
this.em(args);
and you would have compile-time checking instead.

Categories

Resources