How do I use a collection to store a delegate? - c#

I wanted to have a hashtable with a string as key and a functionpointer (delegate) as value.
This way calling the correct routine given a string based command.
However, the compiler won't eat it.
What am I doing wrong?
//declaration
public delegate void categoryHandler(String request);
//init code
Hashtable categories = new Hashtable();
categories.Add("campaigns", Campaigns.post);
//function call
String category = "campaigns";
categoryHandler handler = (categoryHandler) categories[category];
if (handler != null)
{
handler(someString);
}
//handler
static public void post(String request)
{
...
}
The error I get is on the line where I put the function in the hashtable:
Error 2 Argument '2': cannot convert from 'method group' to 'object'
I'm hoping it is just some semantic thingy I forgot...
But if this can't be done... is there another way to have some kind of String based jumptable?

The problem is that you're using Hashtable which is weakly typed. The compiler sees the method group (the name of the method you want to convert into a delegate) but doesn't know what delegate type you mean.
If you want to keep using Hashtable, you could do:
categoryHandler handler = Campaigns.post;
categories.Add("campaigns", handler);
or
categories.Add("campaigns", new categoryHandler(Campaigns.post));
In both cases, the method group is being convert to the specific delegate type, so it's okay.
However, a better solution is to use Dictionary<string, categoryHandler> in the first place - always use strongly typed collections where you can sensibly do so (which is almost always). For the sake of convention, it should be CategoryHandler btw - it's the name of a type. Likewise post should be Post.
Then to call it, you'd use:
String category = "campaigns";
CategoryHandler handler;
if (categories.TryGetValue(category, out handler))
{
handler(someString);
}

If you are using .Net 3.5, you can do what I do when I want to eliminate switch statements:
private readonly Dictionary<string, Action<string>> _lookupTable = new Dictionary<string, Action<string>>
{
{"campaigns", post}
{"somethingElse", doSomethingElse}
{"tryIt", val => doSomethingWithVal(val)}
};
then, where I would have a switch statement, I would do this:
_lookupTable["foo"]("bar");

Don't use a hashtable use the Dictionary
Your code will change too.
//declaration
public delegate void categoryHandler(String request);
//init code
Dictionary<string, categoryHandler> categories = new Dictionary<string, categoryHandler> ;
categories.Add("campaigns", Campaigns.post);
//function call
string category = "campaigns";
if (!categories.ContainsKey(category))
{
// Key not there just return
return;
}
categoryHandler handler = categories[category]; // NO need to cast here
if (handler != null)
{
handler(someString);
}
//handler
static public void post(String request)
{
...
}

Depending on the version of C# that you're using, you may need to do:
categories.Add("campaigns", new categoryHandler(Campaigns.post));
As an aside, if you're using .NET 2.0 or above, you should be using the generic Dictionary<T,T> class instead of Hashtable.

Related

Add methods to dictionary for user selection

Is it possible to add methods directly to a Dictionary? without having to use a more complex delegate solution. I get the error: "cannot convert to class.method".
I have a class with a number of methods, given a users input one of these methods should be selected:
Code:
class CTARules
{
public static void TwentyFiftyMA()
{
//do stuff
}
public static void TwentyHundredMA()
{
//do stuff
}
}
List<Data_Raw> myList = new List<Data_Raw>();
Dictionary<string, CTARules> rulesDictionary = new Dictionary<string, CTARules>(){ };
rulesDictionary.Add("twentyFifty", CTARules.TwentyFiftyMA());
rulesDictionary.Add("twentyHundred", CTARules.TwentyHundredMA());
The idea is of course that if the user selects the string which equals the key of the Dictionary, I can easily fetch the corresponding method to run.
Also, why can't I create my dictionary at class level?
EDIT:
I changed the methods to be called to:
public static List<Data_Result> TwentyFiftyMA(List<Data_Raw> myRawData)
{
List<Data_Result> ResultList = new List<Data_Result>();
//do stuff with lists
return ResultList;
}
public static List<Data_Result> TwentyHundredMA(List<Data_Raw> myRawData)
{
List<Data_Result> ResultList = new List<Data_Result>();
return ResultList;
}
I still get the error: Argument 2: cannot convert from 'method group' to 'Action'. The methods must be able to receive (a) parameters, they can be void methods - same error.
Your methods are convertible to Action delegate, so you can use:
Dictionary<string, Action> rulesDictionary
While adding methods, do not call them, that will return the result of the method, you can use method groups:
rulesDictionary.Add("twentyFifty", CTARules.TwentyFiftyMA);
And why can't I create my dictionary at class level?
Because all executable code should be inside of a method. The declarations inside a class are just metadata, not executable code. You can initialize fields in the class level but that's just a syntactic sugar, the compiler emits the code to the class's constructor.

Pass typeof(x) type to generic method

I've made some Middleware that logs all actions taken by a user within my application. Depending on the action taken, I need parse out some [FromBody] JSON into their respective key/value pairs for logging.
I need to deserialize my JSON within the middleware, but in order to do that, I need to send my DtoType along to the deserializer in order for it to parse out my key/values. I've got a method setup to do that, but I need to pass in a generic type because this will be different for every single action the user takes. (e.g. I have a UserDto, CustomerDto, etc...)
I've setup a dictionary in order to get the type that I need, however when I pass the var to my logging method to do the rest of the work, I get an error stating that this is not a type but a variable. This is true, however I have no idea how I'm supposed to get the type that I pulled out of my dictionary into the method generic type.
See my code below:
LoggingMiddleware.cs readonly dictionary
private readonly Dictionary<string, Type> _postDictionary = new Dictionary<string, Type>
{
{ "path/customers", typeof(CustomerPostDto) },
...//More entries//...
};
LoggingMiddleware.cs Invoke Method
public async Task Invoke(HttpContext context)
{
using (var streamCopy = new MemoryStream())
{
...//Do some stuff here//...
//Logging Actions
if (request.Path != "/")
{
if (request.Method == "POST")
{
Type T = _postDictionary[path];
logAction<T>(contextDto);
}
}
...//Do some stuff here//...
}
}
LoggingMiddleware.cs logAction Method
private void logAction<T>(object contextDto)
{
var dto = ControllerBase.ParseBody<T>(contextDto.Body);
...//Do some stuff here//...
}
EDIT: Following Example of Possible Duplicate - updated code
if (request.Method == "POST")
{
Type T = _postDictionary[path];
MethodInfo methodLogAction = typeof(LoggingMiddleware).GetMethod("logAction", BindingFlags.NonPublic);
MethodInfo generic = methodLogAction.MakeGenericMethod(T);
generic.Invoke(contextDto, null);
}
The above never returns anything for GetMethod other than null.
The exception is telling you exactly what is wrong.
Type T = _postDictionary[path];
This line of code pulls a Type instance from the dictionary and stores it in the variable, T. Then, you try to use it like this:
logAction<T>(contextDTO);
However, a generic method expects a non-variable argument between the angle-brackets. Types don't change at run-time; but the type arguments to a generic method can. (There are some compiler-specific nuances to that statement, but we'll ignore those for now.)
What you're essentially trying to get at is this:
logAction<SomeType>(contextDTO);
But if you want to store the type in a Dictionary, you'll have to pass that type as an argument to your method, and lose the generic capability:
public void logAction(Type type, object data)
{
// Log the data here
};
This is because the value of T is only known at runtime, not at compile time. You're going to have to reflect over T to get at its properties (as your question implies). In that event, you likely don't want a generic method, anyway.
If you're using json.net you could do something like:
public void LogAction<T>(string contextDto, Type type)
{
T obj = (T)JsonConvert.DeserializeObject(contextDto, type) ;
}
Or If I'm reading this wrong, and you and you want something like this, you could do this.
public void LogAction<T>(T obj)
{
}
public ActionResult Test([FromBody] Thing thing)
{
LogAction(thing);
}
I was able to get this with help of the Duplicate Post.
Within my Invoke method, I used GetMethod to find my method and assign a generic type based on my dictionary. Since it was a private method, I had to use both the BindingFlags.NonPublic & BindingFlags.Instance flags in order for it to find the method.
//Logging Actions
if (request.Path != "/")
{
if (request.Method == "POST")
{
Type T = _postDictionary[path];
MethodInfo methodLogAction = typeof(LoggingMiddleware).GetMethod("LogAction", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] {typeof(object)}, null);
MethodInfo generic = methodLogAction.MakeGenericMethod(T);
generic.Invoke(this, new object[]{contextDto});
}
}
There is a difference between a Type (a class) and a generic type T. The T you are trying to get from your dictionary is simply a normal variable of the class Type, and not anything you can pass as a generic type parameter. You would probably have to change your approach a bit in order to achieve what you want without using reflection.
Approach 1. Let LogAction take a Type as a parameter and hope that there is an overloaded version that accepts such an argument:
private void LogAction(object contextDto, Type type) {
ControllerBase.ParseBody(contextDto.Body, type);
}
Or you can look into using a Func to control your parsing behavior better, something like
// Method to get a Func that can parse your object
private static Func<System.IO.Stream, T> GetParser<T>()
{
return (contextDto) => ControllerBase.ParseBody<T>(contextDto.Body);
}
// Use that in your dictionary
private Dictionary<string, Func<System.IO.Stream, object>> transformers = new Dictionary<string, Func<System.IO.Stream, object>>
{
{ "/myPath", GetParser<CustomerPostDto>() },
{ "/myPath-2", GetParser<CustomerPostDto>() }
};
// Now the LogAction method can just take the DTO that will be inferred from our parser
private void LogAction<T>(T dto)
{
...//Do some stuff here//...
}
// And call it as such
if (transformers.ContainsKey(path))
LogAction(transformers[path](context.Response.Body));
I would recommend it over reflection as it should give you more control in the long run.
You can get some more fun and abstraction by separating what is logging, and the other unrelated code:
// Return a logger with a specification on how to parse a stream from a body
private static TypeLogger CreateLogger<T>()
{
return new TypeLogger<T>((ctx) => ControllerBase.ParseBody<T>(contextDto.Body));
}
// Create a list with paths and loggers of specified type
private Dictionary<string, TypeLogger> loggers = new Dictionary<string, TypeLogger>
{
{ "/path1", CreateLogger<CustomerPostDto>() },
{ "/path2", CreateLogger<CustomerPostDto>() },
};
// Abstract base logger class that allows you to log from a requestbody
public abstract class TypeLogger
{
public abstract void Log(System.IO.Stream requestBody);
}
// An actual implementation to parse your dto using by using the parser previously specified
public class TypeLogger<T> : TypeLogger
{
// Parser to use when getting entity
public Func<System.IO.Stream, T> Parser { get; private set; }
// Constructor that takes sa func which helps us parse
public TypeLogger(Func<System.IO.Stream, T> parser) => Parser = parser;
// The actual logging
public override void Log(System.IO.Stream requestBody)
{
var dto = Parser(requestBody);
//...
}
}
// And usage
if (loggers.Contains(path))
loggers[path].Log(ctx.Response.Body);

C# Is there a way to obtain a reference for a field from a class and use the reference to call methods? [duplicate]

This question already has answers here:
Setting/getting the class properties by string name [duplicate]
(3 answers)
Closed 5 years ago.
C# Question .Net version 4.6.1
I have a class as shown below and in a function I create an instance of the class. I would like to create a reference to field(s) from the class instance and call the method "Insert" on the class variables "Name" and "Description".
I used a google search for: "Get reference to class field via
reflection in C#". I did find information on stackoverflow for reflection but it does not talk about calling methods. I was able to use some of it to find what looks like a pointer. However I could not make it all the way to calling methods on the reference. I either am asking Google the wrong question or what I am trying to do is not possible.
C# Reflection - Get field values from a simple class
Class AClass1
{
public String Action = String.Empty;
public List<KeyValuePair<String, String>> Description = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<String, String>> Name= new List<KeyValuePair<string, string>>();
}
// Psudo code:
// The code has been reduced down to show the problem. Error handling
// etc has been removed. This may not compile because I had to
// reduce down the code to show what I am trying to accomplish. Any
// suggestions for creating a reference to a class variable and calling
// its methods would be appreciated. If needed I can create a small
// sample that does compile but I think the sample provided shows
// what I am trying to accomplish. If not let me know and I will
// scale down further.
public void ProcessFeature1(String fieldName, String value1, String value2)
{
AClass1 featureToManage = new AClass1();
KeyValuePair<string, string> entry = new KeyValuePair<string, string>(value1, value2);
if (entry != null)
{
// something along these lines is preferred if possible
var ptrField = featureToManage.GetType().GetField(fieldName).FieldHandle;
if (ptrField != null)
{
// the problem is figuring how to turn the value from reflection into
// something I can call directly. I have tried casting to the same
// type as the variable in the class (not shown here)
// but it did not work or I did it wrong. When I say it does not
// work the run-time information does not show the method and the
// compiler complains generally saying no such method Insert found.
ptrField.Insert(0, entry);
}
else
{
// this works but would prefer to lookup the
// the field in the class and call methods so I
// 1. have less code
// 2. do not have to hard code the name of the field.
switch (fieldName)
{
case "Name":
{
featureToManage.Name.Insert(0, entry);
break;
}
case "Description":
{
featureToManage.Description.Insert(0, entry);
break;
}
default:
{
// something bad happened
break;
}
}
}
}
}
It sounds like you are looking for the GetValue() method.
Once you have gotten the FieldInfo from the Type using GetField(), you just need to call its GetValue() method, passing the object instance that you want to get the field value from (in this case featureToManage), and then cast the resulting value to the appropriate type (in this case List<KeyValuePair<string, string>>).
Here is what it looks like in code:
public void ProcessFeature1(string fieldName, string value1, string value2)
{
AClass1 featureToManage = new AClass1();
// Get the FieldInfo for the requested fieldName
FieldInfo fieldInfo = featureToManage.GetType().GetField(fieldName);
// Use the fieldInfo to get the value of the field from the target instance
var targetList = (List<KeyValuePair<string, string>>)fieldInfo.GetValue(featureToManage);
// now we can use the value as normal
var entry = new KeyValuePair<string, string>(value1, value2);
targetList.Insert(0, entry);
}
Note that in C# 6 you can use the nameof() operator to avoid hardcoding class, field and property names when using reflection, e.g.:
ProcessFeature1(nameof(AClass1.Description), "foo", "bar");

How can i cast from Func<Object> to Func<dynamicType> c#

Hello this is my first post, don't be to hard if I do anything wrong :D
I am writing a DeSerializer for a big Programm,
To do so, I have a own Class
public class DeSerializeableElement
{
public Func<Object> action;
public Type type;
public DeSerializeableElement( Func<Object> p_action,Type p_type)
{
type = p_type;
action = p_action;
}
I read a String and then it always starts with 0XXX a 4 digit number.
with this number I get the right method from my
Dictionary<int,DeSerializableElement>
the initialize of the Dictionary is auto Generated and has 300 elements
deSerializableObjectDictionary.Add(276, new DeSerializeableElement(GetString, typeof(System.String)));
GetString is a method with no parameters and returns a String
Now my problem, if I Deserialize a List, at the moment I create a DeSerializableElement the Func looses its return value Information. Because I save it as Func so I get back a List
but it`s Important to get a List in case of GetString
there are also GetInt or GetDouble and lots more
So If i call GetList(GetString) I want as return value a List
and if I call GetList(GetInt) I want a List and so on.
But I get always a List because my SerializableElement has Func as attribute
The call for GetList looks like
GetList(deSerializableObjectDictionary[objectIdent].action);
GetList looks like
public IList<T> GetList<T>(Func<T> p_action) //T is always Object because of Func<Object> here I the right Type
{
IList<T> list = new List<T>();
ExpectToken('['); //The start Token of a serialized List
while (!IsNextToken(']')) //End Token of serialized List
{
list.Add(p_action());
}
return lst;
}
Any road you choose, it will one such that you are losing type safety. For example, you can defer to a Dictionary<int, object> and wrap that with a GetList<T> method where T is the actual type you want. A mis-use of this method can lead to runtime exceptions.
An example would be:
void Main()
{
var idToFunc = new Dictionary<int, object>();
idToFunc.Add(1, new DeSerializeableElement<int>(() => 1));
Console.WriteLine(GetList<int>(((DeSerializeableElement<int>) idToFunc[1]).Func));
}
public class DeSerializeableElement<T>
{
public Func<T> Func { get; set; }
public DeSerializeableElement(Func<T> func)
{
Func = func;
}
}
I would definitely of the risks involved with this kind of code. Although possible, I would advise you to re-think what you're doing and the architecture of your deserializer.

C# Creating a basic event manager using Action, passing optional params to Invoke

I'm trying to create a simple event manager, but I'm struggling to create it the way I want.
Here is what I have so far, and it works. However, I can not figure out how I can allow for different parameters and them being optional.
using System.Collections.Generic;
using System;
public static class Event_Manager {
public static Dictionary<string, Action> event_list = new Dictionary<string, Action>(){
{
"enemy_killed", null
},
{
"enemy_spawned", null
}
};
public static void on(string evt, Action act){
Action item;
if(event_list.TryGetValue(evt, out item)){
event_list[evt] += act;
}
}
public static void off(string evt, Action act){
Action item;
if(event_list.TryGetValue(evt, out item)){
event_list[evt] -= act;
}
}
public static void trigger(string evt){
Action item;
if(event_list.TryGetValue(evt, out item)){
item.Invoke();
}
}
}
Example of using it:
public void some_method(){
// Do something when an enemy has been killed;
}
Event_Manager.on("enemy_killed", some_method);
Event_Manager.trigger("enemy_killed");
What I would like is to be able to do is pass different types of parameters as well (or some sort of object that could be an event that the methods receive).
public void player_damaged(int damage){
// Reduce health
}
Event_Manager.on("player_hit", player_damaged);
Event_Manager.trigger("player_hit", 15);
Any help would be much appreciated.
Thanks
As you surely have noticed the delegate type Action has no parameters. Keep that in mind.
One solution is to pass an object with the extra parameters. The detail with this is that you don't know the types or even the quantity of the parameters you need.
In .NET this was solved by having EventArgs, and for each new event that needs
a different combination of extra parameters a derived type is created.
That means you would use it like:
Event_Manager.trigger("player_hit", new PlayerHitEventArgs(15));
Where PlayerHitEventArgs is a class that inherits from EventArgs, and the trigger method takes an EventArgs. Similary, you would be using Action<EventArgs> (both in parameters and in the internal dictionary).
For what I get, you want to avoid the hassle.
The next option is to always pass an object, then the recieving party will have to check the type and try to cast it. Or better yet, pass dynamic.
In that case you would be using Action<dynamic>, the trigger method would take dynamic, and now you can pass anonymous types:
public void player_damaged(dynamic data){
var damage = data.damage;
// Reduce health
}
Event_Manager.on("player_hit", player_damaged);
Event_Manager.trigger("player_hit", new {damage = 15});
Note: also change the type of the dictionary accordingly.
If you want the code to be able to detect how many parameters does the act method have, and try to pass the parameters accordingly you will need a bit of reflection.
First you will have to relax the type from Action to simply Delegate because you will be passing things that take all amounts of parameters.
Then, in order to invoke, you first need to read what parameters does the current delegate have. To do that, you will have to get the MethodInfo of the delegate:
MethodInfo methodInfo = item.Method;
And you also need the target object of the delegate, because it may not be an static method:
object target = item.Target;
Now, we can read the parameter list frm the MethodInfo:
var paramList = method.GetParameters();
We have to build an array to invoke the method, the size we get from the list:
var args = new object[paramList.Length];
And start populating it with the values from the object. There is no need to use dynamic here.
Code for trigger:
public static void trigger(string evt, object obj){
Delegate item;
if(event_list.TryGetValue(evt, out item)){
// Get MethodInfo and Target
MethodInfo methodInfo = item.Method;
object target = item.Target;
// Get the parameter list
var paramList = methodInfo.GetParameters();
// Get the type of the obj
var type = obj.GetType();
// Build the argument list
var args = new object[paramList.Length];
for (int index = 0; index < paramList.Length; index++)
{
var parameter = paramList[index];
var name = parameter.Name;
// Get the value from obj
var property = type.GetProperty(name);
var value = property.GetValue(obj, null);
args[index] = value;
}
// Invoke
methodInfo.Invoke(target, args);
}
}
Note: no exception handling. Also remember to undate the dictionary that holds the delegates.
Example usage:
public void player_damaged(int damage){
// Reduce health
}
Event_Manager.on("player_hit", new Action<int>(player_damaged));
Event_Manager.trigger("player_hit", new {damage = 15});
The property damage passed in trigger is mapped to the parameter damage in player_damage by name. I tested this works.
Note: Since on would be taking Delegate the compiler can't choose a delegate type for the "method group", so the cast to a delegate type is needed.
Then you need to change your dictionary into
Dictionary<string, Action<object>>
and trigger method would be like
public static void trigger(string evt, object parameter){
Action<object> item;
if(event_list.TryGetValue(evt, out item)){
if (item != null) {
item.Invoke(parameter);
}
}
}
But in this case you lose all beauty of strongly typed approach, and you will need to do unboxing from the object.

Categories

Resources