Variable as function call from external XML file - c#

I am writing a text file parser for a specific matching condition, and in a couple of the files I need to do some custom manipulation. What I would like to do is store the name of the custom procedure that is being used in an external XML file with the other rules. Most won't use this, and I found this answer regarding the action call:
Variable for function names
The above has the following dictionary definition
private static readonly IDictionary<string,Action<string>> actionByType =
new Dictionary<string,Action<string>>
Element adding from XML file in my current program (These two elements will be added)
foreach (XmlNode node in nodes)
{
Client holding = new Client();
holding.has_custom =
Convert.ToBoolean(
node.SelectSingleNode("has_custom").InnerText);
holding.custom_call =
node.SelectSingleNode("custom_call").InnerText;
clients.Add(holding);
}
As I go through this, how do I assign the name of the custom call as an action to be called in the dictionary? And then do I use a case statement with the generic parse as the default?

Im not sure if I understand you correctly, but you can assign Actions / functions (Delegates to be more specific) like this:
actionByType.Add("write", text => Console.WriteLine(text));
actionByType.Add("write2", Console.WriteLine);
or like this:
void someAction(string someString)
{
Console.WriteLine(someString);
}
...
actionByType.Add("write", someAction);
Then Invoke like this:
actionByType["write"]("Hello World!");
So in your case it would be:
actionByType[holding.custom_call]("What ever you need that string argument for");
Here is the fiddle https://dotnetfiddle.net/oFuEeF

First, get the proper MethodInfo using reflection. This should be a static method, and should reside in a static class containing all your XML-accessible methods.
var method = typeof(MyStoredTypes).GetMethod(methodName, BindingFlags.Public | BindingFlags.Static)
You'll also need a ParameterExpression to capture the incoming string.
var param = Expression.Parameter(typeof(string));
Finally, System.Linq.Expression.Call to create the Expression tree for your call, Lambda it, and Compile it.
var act = Expression.Lambda<Action<string>>(
Expression.Call(param, method),
new ParameterExpression[] { param })
.Compile();

Related

How to execute static methods by name

I have an XML file with classes name like this:
<ActiveMonitorsList>
<MonitorName>CertificatesMonitor</MonitorName>
<MonitorName>ServicesMonitor</MonitorName>
<MonitorName>LogsMonitor</MonitorName>
<MonitorName>DBMonitor</MonitorName>
</ActiveMonitorsList>
Each of this classes containts a method: bool SingleCheck();
I would like to execute this bool SingleCheck() method for each class that is in this XML file.
What is the best way to do this?
This is what I have so far - it doesn't work:
foreach (string monitorName in monitorsList)
{
Type thisType = GetType();
MethodInfo singleMonitorMethod = thisType.GetMethod("{monitorName}.SingleCheck");
bool methodResult = singleMonitorMethod.Invoke(...);
}
In place of (...) - don't know what to put here, but I want to get
the result of the method (it's always bool).
All of those methods I want to pass as paramters are static.
I guess delegates, Actions or Func<> have to go in here...
Thank You very much in advance!
Edit: Each name in XML points to a separate class. Each class have the same named method: public static bool SingleCheck().
What I want to do is:
get all the monitors names (classes names will be the same)
invoke a method (it has the same name in each class) inside EVERY
class present on that list.
EDIT - PROBLEM SOLVED:
When I first created my project, I included separate folder for all monitors. Then I changed my mind, deleted this folder and added manually SAME FILES to my solution. In this way - those files still had "using <namespace>.Monitors"...
And that's why I couldn't list those classes and the Types were still nulls...
Thanks for all suggestions ! ;)
I would suggest to take this overload of the method Invoke It wants an object(calling instance) and a set of input parameters for the method from you.
Since it is a static method, you can calmly pass null as the first parameter and because you method does not have any parameters you again can calmly pass null as the second value. Don't forget to cast object to the corresponding return type. In your case bool.
bool methodResult = (bool)singleMonitorMethod.Invoke(null, null);
To get the correct Type you actually need to know the namespace! So this would look like this:
foreach (string monitorName in monitorsList)
{
string typeName = $"{yourNameSpace}.{monitorName}";
Type thisType = Type.GetType(typeName);
MethodInfo singleMonitorMethod = thisType.GetMethod("SingleCheck");
bool methodResult = (bool)singleMonitorMethod.Invoke(null, null);
}
If the loop is in the same namespace this should also work:
Type thisType = Type.GetType($"{GetType().Namespace}.{monitorName}");
thisType.GetMethod("{monitorName}.SingleCheck") won't work because of two reasons. 1) You forgot the string interpolation $-sign and thus are searching for a method called "{monitorName}.SingleCheck" which obviously can't exist with such a name. 2) Instead of thisType you need to provide the type containing the method.
Invoke needs to be called with the instance as first parameter - null for static methods - and an object array for the method parameters.
Assuming that your monitor classes are in the same assembly like your current type you would need to do the following:
foreach (string monitorName in monitorsList)
{
Type monitorType = GetType().Assembly.GetExportedTypes().Single(x => x.Name == monitorName);
MethodInfo singleMonitorMethod = monitorType.GetMethod("SingleCheck");
bool methodResult = (bool)singleMonitorMethod.Invoke(null, Array.Empty<object>());
}
I prefer Array.Empty over new object[0] or new object[] { } because it doesn't create a new object every time.
Edited: Changed the type discovery according to Mong Zhu's comment that GetType(monitorName) does need the fully-qualified name.

System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'

I am trying to get the MethodInfo from a method TableExists<T> so I can call it with a type.
The method is declared inside OrmLiteSchemaApi class. There are 2 overloads:
public static bool TableExists<T>(this IDbConnection dbConn)
{
// code omitted
}
public static bool TableExists(this IDbConnection dbConn, string tableName, string schema = null)
{
// code omitted
}
I am trying to get the MethodInfo like this:
var tableMethod = typeof(OrmLiteSchemaApi).GetMethod("TableExists");
But it generates exception:
System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'
I could only find an old question related to this that suggested to pass an empty object array as parameter but this doesn't seem to work for .net core.
I guess I need to specify the specific overload but I am not sure exactly how.
How do I get the MethodInfo?
You can use GetMethods (plural!) to get an array of all matching methods, and then look for the one which has IsGenericMethod:
var tm = typeof(OrmLiteSchemaApi)
.GetMethods()
.Where(x => x.Name == "TableExists")
.FirstOrDefault(x => x.IsGenericMethod);
I recommend this over using parameter specifiers, since it'll give you an object you can step through at debug time if there are ever any problems.
Passing an empty object array would only work if you're looking for a function with no parameters. Instead, you need to use a different overload of GetMethod that specifies the types of parameters as a type array. That way you can tell it which reference to get by specifying which types of parameters it should look for.

When compiling C# expression trees into methods, is it possible to access "this"?

I am trying to dynamically generate a class that implements a given interface. Because of this, I need to implement some methods. I would like to avoid directly emitting IL instructions, so I am trying to use Expression trees and CompileToMethod. Unfortunately, some of these methods need to access a field of the generated class (as if I wrote this.field into the method I am implementing). Is it possible to access "this" using expression trees? (By "this" I mean the object the method will be called on.)
If yes, what would a method like this look like with expression trees?
int SomeMethod() {
return this.field.SomeOtherMethod();
}
Expression.Constant or ParameterExpression are your friends; examples:
var obj = Expression.Constant(this);
var field = Expression.PropertyOrField(obj, "field");
var call = Expression.Call(field, field.Type.GetMethod("SomeOtherMethod"));
var lambda = Expression.Lambda<Func<int>>(call);
or:
var obj = Expression.Parameter(typeof(SomeType));
var field = Expression.PropertyOrField(obj, "field");
var call = Expression.Call(field, field.Type.GetMethod("SomeOtherMethod"));
var lambda = Expression.Lambda<Func<SomeType, int>>(call, obj);
(in the latter case, you'd pass this in as a parameter, but it means you can store the lambda and re-use it for different target instance objects)
Another option here might be dynamic if your names are fixed:
dynamic obj = someDuckTypedObject;
int i = obj.field.SomeOtherMethod();

Disabling the functions in a string c#

I know that we can call functions with their name stored in a string like this:
Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod(TheCommandString);
theMethod.Invoke(this, userParameters);
Is their any way in C# by which I can call all the functions in the class except the function in a string?
Want it in late binding, as I have array of strings which includes the methods name which needs to be discarded from the execution.
If you are trying to use reflection to execute all methods except a particular one, you just need to get all methods and exclude the one(s) you're not interested in. For example to exclude a single method name:
var methods = this.GetType().GetMethods()
.Where(t => t.Name != "Whatever");
foreach(var method in methods)
{
method.Invoke(this, userParameters);
}
If you have an list of method names, then you just need to change the filter, for example:
var methodNames = new [] { "Method1", "Method2" };
var methods = this.GetType().GetMethods()
.Where(t => !methodNames.Contains(t.Name);

Can Action.ToString() return anything other than "System.Action"?

I'm programming in Unity, using an Action event to hold a bunch of other Action delegates, in order to hook non-Monobehaviour objects into the Update() system. I'd like to be able to print the names of the actions to the Debug console, but using something like:
Delegate[] actions = updateActions.GetInvocationList();
foreach ( Delegate del in actions ) {
Debug.Log( del.ToString() );
}
... just returns "System.Action". I've tried also (del as Action).ToString() with no luck.
You can use the Method property to get a MethodInfo which should have a useful name.
Delegate[] actions = updateActions.GetInvocationList();
foreach ( Delegate del in actions )
{
Debug.Log( del.Method.ReflectedType.FullName + "." + del.Method.Name );
}
You can use del.Method.ToString() if you want the signature or del.Method.Name if you only want the name. del.Method.ReflectedType.FullName gives you the type name.
For lambdas/anonymous methods the name might not be too useful since they only have a compiler generated name. In the current implementation the name of a lambda is something like <Main>b__0 where Main is the name of the method that contains the lambda. Together with the type name this should give you a decent idea which lambda it is.
If you mean that you declare a delegate
var foo = new Action(() => { /* do something */ });
and you want to retrieve the word "foo" later, you're out of luck. To get that behavior you'll have to consume the declaration as an expression tree and parse out foo yourself.

Categories