Call a method dynamically based on some other variable [duplicate] - c#

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to Call method using its name?
Getting sick of using switch/case statements. I'm wondering if there is some way to call a method based on the value provided by the user. Understand that there could be a million reasons why this is a bad idea, but here's what I'm thinking:
Console.Write("What method do you want to call? ");
string method_name = Console.ReadLine();
then somehow call the method contained in 'method_name'. Is this even possible?

You can use reflection:
var type = typeof(MyClass);
var method = type.GetMethod(method_name);
method.Invoke(obj, params);
If you want the type to be dynamic as well as the method then use this instead of typeof(MyClass):
var type = Type.GetType(type_name);

Many times you can refactor switch statements to dictionaries...
switch (caseSwitch)
{
case 1:
Console.WriteLine("Case 1");
break;
case 2:
Console.WriteLine("Case 2");
break;
case 3:
Console.WriteLine("Case 3");
break;
}
can become ...
var replaceSwitch = new Dictionary<int, Action>
{
{ 1, () => Console.WriteLine("Case 1") }
{ 2, () => Console.WriteLine("Case 2") }
{ 3, () => Console.WriteLine("Case 3") }
}
...
replaceSwitch[value]();
This is a very subtle shift that doesn't seem to gain much, but in reality it's much, much better. If you want to know why, this blog post explains it very well.

Instead of reflection, if you have to act on the user's input value w/o using switch statement, you could use a dictionary having the list of methods mapped against the input value.
private static void Method1(int x)
{
Console.WriteLine(x);
}
private static void Method2(int x)
{
}
private static void Method3(int x)
{
}
static void Main(string[] args)
{
Dictionary<int, Action<int>> methods = new Dictionary<int, Action<int>>();
methods.Add(1, Method1);
methods.Add(2, Method2);
methods.Add(3, Method3);
(methods[1])(1);
}

Your sample
public class Boss
{
public void Kick()
{
Console.WriteLine("Kick");
}
public void Talk(string message)
{
Console.WriteLine("Talk " + message);
}
public void Run()
{
Console.WriteLine("Run");
}
}
class Program
{
static void AutoSwitch(object obj, string methodName, params object[] parameters)
{
var objType = typeof(obj);
var method = objType.GetMethod(methodName);
method.Invoke(obj, parameters);
}
static void Main(string[] args)
{
var obj = new Boss();
AutoSwitch(obj, "Talk", "Hello World");
AutoSwitch(obj, "Kick");
}
}

Another interesting way I have seen to handle(read avoid) switch statements differently is to use a dictionary of methods. I stole this from http://www.markhneedham.com/blog/2010/05/30/c-using-a-dictionary-instead-of-if-statements/ and it looks like they are using the MVC framework but the same basic principal applies
public class SomeController
{
private Dictionary<string, Func<UserData,ActionResult>> handleAction =
new Dictionary<string, Func<UserData,ActionResult>>
{ { "Back", SaveAction },
{ "Next", NextAction },
{ "Save", SaveAction } };
public ActionResult TheAction(string whichButton, UserData userData)
{
if(handleAction.ContainsKey(whichButton))
{
return handleAction[whichButton](userData);
}
throw Exception("");
}
private ActionResult NextAction(UserData userData)
{
// do cool stuff
}
}

If you're thinking you could somehow do this:
Console.Write("What method do you want to call? ");
string method_name = Console.ReadLine();
method_name();
You are mistaken. You have to analyze the user input and call a method based on that.

Sure, reflection is your friend. Have a look at Type.GetMethod().

Related

looking alternative way for this switch case or any other solution

I am doing an database update operation and updating some of the fields based on type being passed to the internal method with switch case statement, at the moment it has 4 case statements and it will grow bigger..
I am looking a way to convert this switch case to dictionary with key value pair or any kind of mechanism to implement inside method.
This is main method
public async Task<MutationResponse> SetRequestStage(string requestStage, Guid requestId, MasterSectionEnum masterSectionEnum)
{
var request = _dbContext.Requests.SingleOrDefault(r => r.Id == requestId);
var rs = _dbContext.RequestStages.SingleOrDefault(rs => rs.Name == requestStage);
if (rs != null)
{
request.RequestStage = rs;
if (rs.Name == "Approved")
{
switch (masterSectionEnum)
{
case MasterSectionEnum.LOCALCODE:
await UpdateRevision<LocalCode>(request.DataId).ConfigureAwait(false);
break;
case MasterSectionEnum.NATIONALCODE:
await UpdateRevision<NationalCode>(request.DataId).ConfigureAwait(false);
break;
case MasterSectionEnum.GUIDELINES:
await UpdateRevision<Guideline>(request.DataId).ConfigureAwait(false);
break;
case MasterSectionEnum.LIBRARYA621:
await UpdateRevision<LibraryA621>(request.DataId).ConfigureAwait(false);
break;
case .....
case .....
default:
throw new ArgumentException($"SetRequestStage Error: invalid MasterSection {masterSectionEnum.ToString()}");
}
}
}
_dbContext.SaveChanges();
return new MutationResponse();
}
and this will be the enum
public enum MasterSectionEnum
{
LOCALCODE,
NATIONALCODE,
GUIDELINES,
SPACETYPE,
LIBRARYA621
// this will grow bigger
}
and this will be the internal method that i am calling from above method
private async Task UpdateRevision<T>(Guid id) where T : class, IAEIMaster, IRevisionData
{
var dbSet = this._dbContext.Set<T>();
var code = dbSet.SingleOrDefault(c => c.Id == id);
......
......
code.Revision = revision;
code.IsApproved = true;
}
could any one suggest on how to convert this switch case to alternative kind with key value pair or with types that would be very grateful to me.
many thanks in advance
Update : i am looking kind of below method, I am using dot net core with EF core
var types = new Dictionary<string, string>();
foreach(var item in types)
{
if(item.Key == "enum value")
{
await UpdateRevision<item.value>(request.DataId).ConfigureAwait(false);
}
}
Instead of passing in the parameter MasterSectionEnum, change your method to accept a type parameter. Then just use that generic type in a single call of UpdateRevision.
public async Task<MutationResponse> SetRequestStage<SectionType>(
string requestStage,
Guid requestId
) {
...
await UpdateRevision<SectionType>(request.DataId).ConfigureAwait(false);
....
}
If all valid types of SectionType share an interface or are derived from the same class, then you can take it further and add a constraint to the SectionType and avoid having to handle bad types at runtime.
Edit:
So SetRequestStage can't be generic huh? Well, the inclination to make it generic stems from the fact that it depends on UpdateRevision, which is generic. This in turn depends on DbContext.Set(). And you're using a generic version of it. But the good news is that there seems to be a non-generic version of it that accepts a type variable as a parameter. So:
async Task UpdateRevision<T>(
Guid id,
Type t
) where T : class, IAEIMaster, IRevisionData {
var dbSet = this._dbContext.Set(t);
...
}
And then:
public async Task<MutationResponse> SetRequestStage(
string requestStage,
Guid requestId,
Type SectionType
) {
...
await UpdateRevision(request.DataId, SectionType).ConfigureAwait(false);
....
}
I don't know what your UI looks like. But, generically:
var dic = new Dictionary<MasterSectionEnum, Type> {
{ MasterSectionEnum.LOCALCODE, typeof(LocalCode) },
{ MasterSectionEnum.NATIONALCODE, typeof(NationalCode) },
...
};
public async someUiRelatedMethod(
string reqStage,
Guid reqId,
MasterSectionEnum sectionType
) {
await SetRequestStage(reqStage, reqId, dic[sectionType]);
}
Sorry if the syntax on the latter isn't quite right. But you get the idea.
You can try this:
public enum MasterSectionEnum
{
LOCALCODE,
NATIONALCODE
}
public sealed class LocalCode { }
public sealed class NationalCode { }
public sealed class Program
{
private static readonly Dictionary<MasterSectionEnum, Func<Guid, Task>> Dictionary
= new Dictionary<MasterSectionEnum, Func<Guid, Task>>();
static Program()
{
Dictionary[MasterSectionEnum.LOCALCODE] = UpdateRevision<LocalCode>;
Dictionary[MasterSectionEnum.NATIONALCODE] = UpdateRevision<NationalCode>;
}
public static async Task SetRequestStage(MasterSectionEnum masterSectionEnum)
{
await Dictionary[masterSectionEnum].Invoke(Guid.NewGuid());
}
private static Task UpdateRevision<T>(Guid id) where T : class
{
Console.WriteLine(typeof(T));
return Task.CompletedTask;
}
public static async Task Main()
{
await SetRequestStage(MasterSectionEnum.LOCALCODE);
await SetRequestStage(MasterSectionEnum.NATIONALCODE);
Console.ReadKey();
}
}
Result:
LocalCode
NationalCode

Extension Methods with variable number of parameters

I have created this helper class RichTextBoxHelper that has an extension method, and I would like to write another WriteLine method or rewrite this one (which solution is best) in order to be able to use it in the function presented under it. Thank you.
public static class RichTextBoxHelper
{
public static void WriteLine(this RichTextBox txtLog, object line)
{
txtLog.AppendText(line + Environment.NewLine);
}
}
private void selectToolStripMenuItem_Click(object sender, EventArgs e)
{
var vehicles = new List<Tuple<string, string, int>>
{
Tuple.Create("123","VW",1999),
Tuple.Create("234","Ford",2009),
Tuple.Create("567","Audi",2005),
Tuple.Create("678","Ford",2003),
Tuple.Create("789","Mazda",2003),
Tuple.Create("999","Ford",1965)
};
var fordCars = vehicles.Where(v => v.Item2 == "Ford")
.Select(v => new Car
{
VIN = v.Item1,
Make = v.Item2,
Year = v.Item3
});
foreach (var item in fordCars)
txtLog.WriteLine("Car VIN:{0} Make:{1} Year:{2}", item.VIN, item.Make, item.Year);
}
Yep, that's completely possible. It's called method overloading and it works just as well on extension method classes as normal classes.
The signature you require for your new method is:
public static void WriteLine(
this RichTextBox txtLog,
string format,
params object[] args)
{
// ...
}
Just put it in the same class as your other one and you'll be able to use both as appropriate.
Alternatively you can call your existing method in the following way:
txtLog.WriteLine(
String.Format(
"Car VIN:{0} Make:{1} Year:{2}",
item.VIN,
item.Make,
item.Year));
I think dav_i answer is correct but I prefer you to write your extension method for IsIn method something like below, because you can use it everywhere for every different kind of variables:
public static class ExtensionMethods
{
public static bool IsIn<T>(this T keyObject, params object[] collection)
{
return collection.Contains(keyObject);
}
}
usage of method is like here:
if (intValue.IsIn( 2, 3, 7 ))
{
do something...
}
if (stringVlaue.IsIn("a","b","c"))
{
do something...
}

Ask a chain/multiple questions together (Voice Recognition)

I need help asking my program a series of questions.
For example:
I may say "hi computer" and I want my computer to respond saying "Hi, Sir. How are you?" Then my computer would say "Fine and yourself?" and my computer would say something else.
As of now I'm using Case statements. An example of my code below:
//Kindness
case "thank you":
case "thank you jarvis":
case "thanks":
case "thanks jarvis":
if (ranNum <= 3) { QEvent = ""; JARVIS.Speak("You're Welcome Sir"); }
else if (ranNum <= 6) { QEvent = ""; JARVIS.Speak("Anytime"); }
else if (ranNum <= 10) { QEvent = ""; JARVIS.Speak("No problem boss"); }
break;
An approach I've had good success with is to create 'Contexts', which are (nested) collections of responses and scripts. When you find a matching Context, you push that Context onto a stack, and start looking for responses in the inner Contexts. If no response matches the current set of contexts, you pop the stack and retry. If the stack is empty, you generate a default "I don't understand" response.
An interesting implementation of this could be based on the answers to this question, particularly this answer, which nicely maps the response/action pairs.
A factory pattern is what you need.
The factory simply reflects on all the methods in MySpeechMethods, looks for ones with SpeechAttributes and sends back the MethodInfo to invoke.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MyApp.SpeechMethods;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
var methods = new MySpeechMethods();
MethodInfo myMethod;
myMethod = SpeechFactory.GetSpeechMethod("Thank you");
myMethod.Invoke(methods, null);
myMethod = SpeechFactory.GetSpeechMethod("Say something funny");
myMethod.Invoke(methods, null);
myMethod = SpeechFactory.GetSpeechMethod("I said funny dammit!");
myMethod.Invoke(methods, null);
}
}
public static class SpeechFactory
{
private static Dictionary<string, MethodInfo> speechMethods = new Dictionary<string, MethodInfo>();
public static MethodInfo GetSpeechMethod(string speechText)
{
MethodInfo methodInfo;
var mySpeechMethods = new MySpeechMethods();
if (speechMethods.Count == 0)
{
var methodNames =
typeof (MySpeechMethods).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
var speechAttributeMethods = methodNames.Where(y => y.GetCustomAttributes().OfType<SpeechAttribute>().Any());
foreach (var speechAttributeMethod in speechAttributeMethods)
{
foreach (var attribute in speechAttributeMethod.GetCustomAttributes(true))
{
speechMethods.Add(((SpeechAttribute)attribute).SpeechValue, speechAttributeMethod);
}
}
methodInfo = speechMethods[speechText];
}
else
{
methodInfo = speechMethods[speechText];
}
return methodInfo;
}
}
}
namespace MyApp.SpeechMethods
{
public class MySpeechMethods
{
[Speech("Thank you")]
[Speech("Thank you Jarvis")]
[Speech("Thanks")]
public void YourWelcome()
{
JARVIS.Speak("You're Welcome Sir");
}
[Speech("Say something funny")]
public void SayFunny()
{
JARVIS.Speak("A priest, a rabbi and a cabbage walk into a bar");
}
[Speech("I said funny dammit!")]
public void TryFunnyAgain()
{
JARVIS.Speak("My apologies sir.");
}
}
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = true)]
public class SpeechAttribute : System.Attribute
{
public string SpeechValue { get; set; }
public SpeechAttribute(string textValue)
{
this.SpeechValue = textValue;
}
}
}

ThreadPool.QueueUserWorkItem with function argument

I am using C# 2.0 and want to call a method with a couple of parameters with the help of ThreadPool.QueueUserWorkItem, so I tried as follows:
ThreadPool.QueueUserWorkItem(new WaitCallback(Multiply(2, 3)));
private int Multiply(int x,int y)
{
int z=(x*y);
return z;
}
I am getting compilation error. So please guide me, how can I call a function with multiple arguments with ThreadPool.QueueUserWorkItem?.
I have another query that when I am using ThreadPool.QueueUserWorkItem then how to use here anonymous function as a result I can write the code there instead of calling another function. If it is possible in C# v2.0 then please guide me with code.
You should declare a method which have the same definition as WaitCallback delegate. You can use the following code snippet:
ThreadPool.QueueUserWorkItem(Multiply, new object[] { 2, 3 });
public static void Multiply(object state)
{
object[] array = state as object[];
int x = Convert.ToInt32(array[0]);
int y = Convert.ToInt32(array[1]);
}
Anonymous delegate version is:
ThreadPool.QueueUserWorkItem(delegate(object state)
{
object[] array = state as object[];
int x = Convert.ToInt32(array[0]);
int y = Convert.ToInt32(array[1]);
}
, new object[] { 2, 3 });
Using a lambda expression would also work
ThreadPool.QueueUserWorkItem(state => Multiply(2,3));
Here's a fuller example which gets the result back to the initial thread, and shows how the delegate can be defined anonymously:
class Program
{
static void Main(string[] args)
{
using (MultiplyTask task = new MultiplyTask() { Multiplicands = new int[] { 2, 3 } })
{
WaitCallback cb = new WaitCallback(delegate(object x)
{
MultiplyTask theTask = x as MultiplyTask;
theTask.Result = theTask.Multiplicands[0] * theTask.Multiplicands[1];
theTask.Set();
});
ThreadPool.QueueUserWorkItem(cb, task);
Console.WriteLine("Calculating...");
if (task.WaitOne(1000))
{
Console.WriteLine("{0} times {1} equals {2}", task.Multiplicands[0], task.Multiplicands[1], task.Result);
}
else
{
Console.WriteLine("Timed out waiting for multiplication task to finish");
}
}
}
private class MultiplyTask : EventWaitHandle
{
internal MultiplyTask() : base(false, EventResetMode.ManualReset) { }
public int[] Multiplicands;
public int Result;
}
}
In my case, I needed an anonymous function. i.e., write to a stream asynchronously. So I used this:
ThreadPool.QueueUserWorkItem(state => {
serializer.Serialize(this.stream);
this.stream.Flush();
});
for passing arguments without lambda - just pack all arguments into a class/object
MyArgumentsClass m_MyArgumentsClass = new MyArgumentsClass();
//fill arguments in class
ThreadPool.QueueUserWorkItem(new WaitCallback(MyFunction), m_MyArgumentsClass);
then, in the function - cast the object into your arguments class
public void MyFunction(object msg)
{
MyArgumentsClass m_MyArgumentsClass = (MyArgumentsClass)msg;

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