My ultimate goal is to create a function that will dynamically pass method names to classes in the Hangfire library.
For example, here is the non-dynamic code which works:
RecurringJob.AddOrUpdate(() => myFunction(), Cron.Hourly)
The type of the first argument for AddOrUpdate is Expression<Action>. My first step was to use reflection to dynamically insert the function name:
Type thisControllerType = this.GetType();
MethodInfo method = thisControllerType.GetMethod(methodName); //methodName passed as string parameter
RecurringJob.AddOrUpdate(() => method.Invoke(this, null), Cron.Hourly);
When I check the Hangfire dashboard, it seems that this expression is being evaluated as MethodBase.Invoke. So I need help passing in the method name dynamically.
That may be enough info to answer my question, but another path I have taken is trying to generate the entire expression for the argument.
RecurringJob.AddOrUpdate(CreateCallExpression(method), Cron.Hourly);
public Expression<Action> CreateCallExpression(MethodInfo method)
{
//trying to pass in zero argument parameter, not sure if this syntax is correct
var parameter = System.Linq.Expressions.Expression.Parameter(typeof (Array));
return System.Linq.Expressions.Expression.Lambda<Action>(System.Linq.Expressions.Expression.Call(method, parameter));
}
In this case I am getting the exception {"Static method requires null instance, non-static method requires non-null instance.\r\nParameter name: method"}. I am working on that, but not sure if this is the road I should be going down. I have been working on this all day, so I was hoping someone might be able to help me speed up my learning.
Your second instance will work in creating a pointer to your specified method, but to solve your static issue you just need to modify the following line in one of 2 ways. First you can complete the static reference by declaring the method you seek to be static and modifying this line of code:
System.Linq.Expressions.Expression.Call(method, parameter);
You would have to provide a null parameter for the call method because if you are searching for a static method, then the compiler will know exactly what method signature you desire, because there will only exist 1. The line of code would be updated to:
System.Linq.Expressions.Expression.Call(null, method, parameter);
The second approach is to define the class or "instance" that correlates to the method so that the compiler knows what class to search against for the method signature. You would have to modify your code like this:
var myInstance = Expression.Parameter(typeof(MyClass), "inst");
System.Linq.Expressions.Expression.Call(myInstance, method, parameter)
I recommend looking at the documentation for Call so that you know exactly how the pointer is being created.
Related
I have a generic as follows.
public class PaginatedList<T> : List<T>
{...}
I simply want to invoke ToList() method on that object at runtime using reflection.
Can someone please help.
I have come so only far.
MethodInfo toListMethod = typeof(Enumerable).GetMethod("ToList");
var constructedToList = toListMethod.MakeGenericMethod(TypeObjectOfT);
constructedToList.Invoke(paginatedListObject, null);
I get exception at the last line with message, Parameter count mismatch. I feel that the first two steps are ok, as I have checked the toListMethod.ToString() and constructedToList.ToString(). And they have given me the following output, which I feel is correct.
System.Collections.Generic.List`1[TSource] ToList[TSource](System.Collections.Generic.IEnumerable`1[TSource])
System.Collections.Generic.List`1[AvbhHis.BL.Entities.PatientCategory] ToList[PatientCategory](System.Collections.Generic.IEnumerable`1[AvbhHis.BL.Entities.PatientCategory])
Questions:
1. Am I right so far?
What should be the parameter to MakeGenericMethod() method. In my case it is the Type of intance of the object of Type T at runtime.
There seems to be some problem with the Invoke method call. Is passing null correct as second parameter? The first parameter should be an object of the type PaginatedList right?
My energy is out, so kindly help.
The first parameter [to Invoke] should be an object of the type PaginatedList right?
ToList is a static method on Enumerable that takes an IEnumerable<T> as it's only parameter:
public static List<TSource> ToList<TSource>(
this IEnumerable<TSource> source
)
Invoke takes the instance as the first parameter and the method parameters after that. For a static method you use null for the "instance" parameter.
So the proper syntax would be
object o = constructedToList.Invoke(null, new object[] {paginatedListObject});
o will then be an object of type List<T> (but you don't know kniw what T is at compile time, so you can't cast it).
List<T> has a constructor that takes an IEnumerable<T> (Which gets called in ToList) so you can simplyfy this task by writing the following:
var resul = Activator.CreateInstance(typeof(List<>).MakeGenericType(TypeObjectOfT), paginatedListObject);
I have been searching for this for some time but not gotten anywhere. I want to do the following:
Given a type say Dictionary<string,MyClass> and say its method ContainsKey(string) - at run time
I want to be able to extract out the 'Generic signature' of this method. That is I want to get
Boolean Dictionary<TKey,TValue>.ContainsKey(TKey) ( and not Boolean ContainsKey(string) )
I know that this is possible by doing the following
var untyped_methods = typeof(DictObject).GetGenericTypeDefition().GetMethods();
// and extract the method info corresponding to ContainsKey
However is is possible to get this information directly from the methods reflected from the
actual type and not from the Generic types ? Meaning Can I get the generic definition
from methods that have been obtained as follows :
var actual_typed_methods = typeof(DictObject).GetMethods()
In essence is it possible to get the "un-typed" method signatures from MethodInfo objects returned in the second snippet above directly (not via comparing the lists and figuring out)
Thanks
EDIT: Maybe this did what you wanted. Not sure if you were actually looking to Invoke or not.
If Invoking, then no (see below). If you just want the definition, then you can use typeof(Dictionary<,>) to get the raw generic definitions.
Unfortunately, no if you are wanting to Invoke.
Demonstration of why:
void Main()
{
var genericDictionaryType = typeof(Dictionary<,>);
var method = genericDictionaryType.GetMethod("ContainsKey");
var dict = new Dictionary<string, string>();
dict.Add("foo", "bar");
Console.WriteLine("{0}", method.Invoke(dict, new[] { "foo" }));
}
Yields the following error:
Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Sounds simple enough to fix. Just call method.MakeGenericMethod(typeof(string)); to get the actual typed MethodInfo object.
Unfortunately again, you cannot.
Boolean ContainsKey(TKey) is not a GenericMethodDefinition. MakeGenericMethod may only be called on a method for which MethodBase.IsGenericMethodDefinition is true.
Reason for this is because the method defined as bool ContainsKey(TKey) instead of bool ContainsKey<TKey>(TKey).
You will need to get the ContainsKey method from the correct Dictionary<TK,TV> signature in order to use it.
Is something like this possible?
//
// create a delegate
Action<Type> action = (t) => t.DoSomething;
//
// get the IL generator for a method
ILGenerator il = myMethodBuilder.GetILGenerator();
//
// try and call the delegate
il.EmitCall(OpCodes.Callvirt, action.Method, null);
Im getting a MethodAccessException whenever I try to invoke the method.
Thanks
Im getting a MethodAccessException whenever I try to invoke the method.
This is because the method generated for the C# (t) => t.DoSomething lambda is private. Chances are this lambda won't be static, either, depending on which of the local variables it captures from the outer method. You're issuing a callvirt instruction but you don't appear to be supplying an instance.
You can verify this by loading your application's code in Reflector and looking at the implementation of your (t) => t.DoSomething lambda.
You need to either:
Upgrade your lambda to a real public static method in an externally-visible class
Find a way to include a parameter of type Action<Type> in your IL method, generate code that calls Action<Type>.Invoke, then pass your action variable into the generated method
See if this is related to what is mention Here.
If you use a dynamic method you can use the skip visibility checks in the jitter.
I have a class that implements an Interface with some methods.
I also have method with an Action parameter where I pass the Interface method.
Is it possible to get the type of the owner that has the method?
EDIT
This is the wrapper:
private void Wrapper (params Action[] actions)
{
var action = actions.FirstOrDefault();
var type = action.Method.DeclaringType.Name;
}
private void test()
{
Wrapper(()=> _GC.ChargeCancellation(""));
}
For demonstration purpose I don't iterate through the collection.
I want the type of _GC.
Actually, I should've spotted this to begin with but since Jon didn't either I'm not feeling too bad about it :)
The code you have to begin with in your question does not compile:
Wrapper(() => TestClass.Hello);
This is not complete code. You either have to have:
Wrapper(TestClass.Hello);
^
|
+-- notice the missing () => here
or:
Wrapper(() => TestClass.Hello());
^
|
+-- notice the added parenthesis here
And now that you have edited your question, it is clear you have the second form.
There's a subtle difference between the two. Subtle to us, but important to the compiler:
Wrapper(TestClass.Hello);
^------+------^
|
+-- This is a method group
Wrapper(() => TestClass.Hello());
^------+--------^
|
+-- This is a method call
A method group is a reference to a method (or its overloads), a method call is executable code.
The difference to the compiler is that in the first piece of code, the compiler will wrap up the method group into an action, basically compile it like this:
Wrapper(new Action(TestClass.Hello));
and thus, you're passing that method to the Wrapper method, inside an Action delegate.
However, the second form is handled altogether differently. The compiler now produces a new method to contain your code, and then passes the new method to the Wrapper method instead of the code you had.
Thus, your code actually looks like this:
public static void Main()
{
Wrapper(new Action(TempMethod1));
}
private static void TempMethod1()
{
TestClass.Hello();
}
And that's why you're seeing the form class as the owner of the method, because that's what it is.
The reason I asked in a comment whether you were taking a delegate or an expression is that my brain was working at half speed. It detected something odd, but not the whole picture.
If you want to pass code like this to a method, and work with it, you have two choices:
For a delegate, work with reflection, decompile the code and analyze it, find the method call and figure out which class it is made on
For an expression, analyze the pieces of the expression (this requires C# 3 or above)
Since neither is trivial I'm not going to post any code here, suffice to say that what you're asking warrants a new question. Of the two, I suggest you go with the expression way if you can.
EDIT: I misread the example, and thought it was using a method group conversion. You can certainly get the method in the delegate itself:
public void Wrapper(Action action)
{
MethodInfo method = action.Method;
Type type = method.DeclaringType; // TestClass
string name = method.Name; // Hello
}
I'm not sure off the top of my head which method will be used if you pass in a delegate instance with multiple actions... but it shouldn't be a problem in this case.
Doh - I misread the question. When you use a lambda expression, that's going to build an extra method in the calling class - and that is the method which contains the reference to _GC.
If you don't want this behaviour, you should change Wrapper to accept params Expression<Action>[] actions - you can then examine the expression trees appropriately and find out the calls that way. It's a bit fiddly sometimes, but it's doable. Look at the Body of the expression tree, which will represent the method call. Note that if you still want to execute the actions, you can call Compile on the expression tree to obtain a delegate.
I'm fairly new to generics so I wonder if somebody can explain the following problem I'm having. In virtually all my controllers in an ASP.NET MVC application I need to return a filtered list (to populate a JqGrid as it happens where the user will have specified certain filtering criteria). Each controllers list method will return a different IQueryable list so I set about creating a generic method to handle this.
While I was creating my method I defined it in a specific controller. Everything compiled and I got the results I expected. Because I want to call this method from all my controllers I assumed I could simply create another static class, put the method in there and then call that method from all my controllers. But if I try to move the method to anywhere else other than the controller thats calling it, the compiler complains about the last line of the method with the following error:
The type arguments for method System.Linq.Queryable.Where<TSource>(System.Linq.IQueryable<TSource>,
System.Linq.Expressions.Expression<System.Func<TSource,bool>>)
cannot be inferred from the usage.
Try specifying the type arguments explicitly.
public static IQueryable<T> FilteredList<T>(IQueryable<T> list, string filters)
{
var qb = new QueryBuilder<T>();
var whereClause = qb.BuildWhereClause(filters);
return list.Where(whereClause);
}
I've tried list<T>.Where(whereClause) and list.Where<T>(whereClause) and just about every other combination, can anyone explain to me where I'm going wrong.
That would suggest that your BuildWhereClause method isn't returning the appropriate type.
The compiler is trying to infer the type of TSource from both list and whereClause. Now whereClause should be an Expression<Func<T, bool>> but I suspect it's not. Hover over the var in the declaration of whereClause to find out what it actually is. I wouldn't recommend using var when the return type isn't obvious.