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.
Related
I have a series of generic methods that accept a type to run. For instance:
db.CreateTable<MyClassName>();
I want to be able to create a list of classes and then iterate over the list like:
foreach(var class in classList)
{
db.CreateTable<class>();
db.CheckStatus<class>();
// ... etc
}
VS says the parameter is a method group. I googled this and docs say this is a delegate. How do i create a list of delegates pointing to my classes that I can then iterate over and use as reference in call to generic method?
My answer makes an assumption I just realized might not be valid: I am assuming that classList is a IEnumerable<Type>. If that is incorrect, my answer will need to be revised.
It needs to be something like this.
var fn = db.GetType().GetMethod("CheckStatus");
foreach (var cls in classList)
{
var fn2 = fn.MakeGenericMethod(cls);
fn2.Invoke(db, null);
}
Okay, what's going on here. We get a reference to the method on the db object called "CheckStatus". If there are overloads of that method, you'll need to use one of the overloads of GetMethod to get it.
Then, we make it a closed generic method. So in the first part, we get a method CheckStatus<T>, where T is not specified. This is an open generic. You need to fill in the generic with your type information. That is what MakeGenericMethod does. In effect, it gives you CheckStatus<cls>. You can then invoke this method on db.
For more information on open/closed generics, see: What exactly is an “open generic type” in .NET?
I've got a generic method that takes an arbitrary JObject (from JSON.net) and converts it into the generically typed object.
So, let's simplify my conversion method to look like the following:
private async Task<T> ConvertToObject(JObject obj) {
//Lots of other stuff yielding in the creation of newObj
var result = newObj.ToObject<T>();
return result;
}
Now this method works fine as-is, but I want to modify the method so I can properly do the same for complex properties within the generic object that aren't available in my JObject (e.g. I have to look them up separately, do this same conversion and apply to this object result).
My approach so far is to loop through all the properties, identify the complex types, perform the lookup to retrieve their values from my data store, then execute the above against them (potentially recursively if they too have any complex objects), then write that value back out to this complex property, repeat for any others and then as before, return that result.
I'm retrieving the properties via Reflection:
var properties = result.GetType().GetProperties();
foreach (var property in properties) {
if (IsSimple(property.PropertyType)
continue;
//Do external lookup
var convertedValue = new ConversionTool<>().Lookup(query);
}
Now, that last line is where I'm having my problem. I'm expected to pass a class name into this, not just a type, but I only know the type at runtime per the reflection methods above. I found a post at http://www.paulbatum.com/2008/08/less-fragile-way-to-invoke-generic.html detailing how to make this work if I were simply passing the generic type into a method and he explains the issue with using Activator.CreateInstance in the comments, but it seems like I know the type I'd want to put there - I just don't know it until it runs (and I retrieve it via reflection).
This seems like an issue that ORMs would run into when it comes to populating complex properties of their entities, but I'm having a difficult time finding how they're doing this.
Given that the caller of this method is aware at runtime what the intended type is, how might I go about passing that type into the generic class constructor so I might call it recursively for each complex member of a given type?
I know I can fetch the method info using GetMethods, but I want to know how to do it properly without GetMethods. I have read other SO questions and answers that suggest this is not possible, or suggest just using LINQ instead, but that isn't really an answer to the question.
Consider at the most basic level, a static generic function that takes a single generic parameter.
private static void Test<T>(T val)
{
}
To fetch this method info we can just call Type.GetMethod("Test", BindingFlags.Static | BindingFlags.NonPublic). However if there were some reason we could not use this simple GetMethod signature (perhaps due to multiple overloads), then we need to supply the parameter types. The problem is that I cannot create a parameter type that accurately matches the T val parameter. What's interesting is that I can take the parameters from the method info (fetched with GetMethods) and pass that into GetMethod to get the desired outcome. This means that if it were only possible to create the appropriate generic types (with IsGenericParameter set to true) then I feel like this would be completely possible.
So that means that this is entirely possible in .NET, and only require the creation of the proper type instances. How does one create these type instances? And if they are not possible to create, why aren't they?
I created a simple fiddle to showcase the issue.
It isn't readily available, because the types you need are actually generic type arguments that only exist in the method / parameter definition. For example, in your Test<T>(T val), the parameter type is "the T as defined by Test<T>. You can't construct that, because it isn't composed from anything. The only way to obtain that T is via GetParameters().
Basically, that leaves: the hard way - i.e. manually. For example:
var method1 = typeof(Program).GetMethods(flags).Single(x => x.Name == "Test"
&& x.IsGenericMethodDefinition && x.GetParameters().Length == 1
&& x.GetParameters()[0].ParameterType == x.GetGenericArguments()[0])
.MakeGenericMethod(paramTypes1);
Obviously it is simpler if you know there is only one Test(...) method:
var method = typeof(Program).GetMethod("Test", flags)
.MakeGenericMethod(paramTypes1);
This isn't calling GetMethod but the "other" way to get a generic method definition is to "cast" a method group to a specific type, then call .Method.GetGenericDefinition() on it.
In your example, the method signature you need is Action<object> where object is just a placeholder and can be any type that matches the constraints of your generic method.
var genericMethodDefinition =
((Action<object>)Test<object>).Method.GetGenericMethodDefinition();
You might select a different overload of "Test" that is defined as private static T Test<T>(T val, int counter) by using the following.
var genericMethodDefinition2 =
((Func<object, int, object>)Test<object>).Method.GetGenericMethodDefinition();
Well, I don't think that it is possible.
But other approach (still hard way, I believe that GetMethods would be better):
var method1 =
typeof (Program).GetMember("Test*",
BindingFlags.InvokeMethod |
BindingFlags.NonPublic |
BindingFlags.Static)
.Cast<MethodInfo>()
.Single(
m =>
m.GetGenericArguments().Length == 1 &&
m.GetGenericArguments()[0].IsGenericParameter)
.MakeGenericMethod(paramTypes1);
Of course, you can omit MakeGenericMethod to get exacly the same result as method2.
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.
What would be the way to call some method by name, like "Method1", if I've got an Object and it's Type?
I want to do something like this:
Object o;
Type t;
// At this point I know, that 'o' actually has
// 't' as it's type.
// And I know that 't' definitely has a public method 'Method1'.
// So, I want to do something like:
Reflection.CallMethodByName(o, "Method1");
Is this somehow possible? I do realize that this would be slow, it's inconvenient, but unfortunately I've got no other ways to implement this in my case.
If the concrete method name is only known at runtime, you can't use dynamic and need to use something like this:
t.GetMethod("Method1").Invoke(o, null);
This assumes, that Method1 has no parameters. If it does, you need to use one of the overloads of GetMethod and pass the parameters as the second parameter to Invoke.
You would use:
// Use BindingFlags for non-public methods etc
MethodInfo method = t.GetMethod("Method1");
// null means "no arguments". You can pass an object[] with arguments.
method.Invoke(o, null);
See MethodBase.Invoke docs for more information - e.g. passing arguments.
Stephen's approach using dynamic will probably be faster (and definitely easier to read) if you're using C# 4 and you know the method name at compile time.
(If at all possible, it would be nicer to make the type involved implement a well-known interface instead, of course.)
The easiest way:
dynamic myObject = o;
myObject.Method1();