How to get MethodInfo from a method symbol [duplicate] - c#

This question already has answers here:
get methodinfo from a method reference C#
(7 answers)
Closed 9 years ago.
Is it possible to get a MethodInfo object from a method symbol?
So in the same vein as:
typeof(SomeClassSymbol) // this gets you a Type object
Here is what I want to do:
public class Meatwad
{
MethodInfo method;
public Meatwad()
{
method = ReflectionThingy.GetMethodInfo(SomeMethod);
}
public void SomeMethod() { }
}
How could I implement ReflectionThingy.GetMethodInfo? Given this is even possible at all, what about overloaded methods?

Delegates contain the MethodInfo you want in their Method property. So your helper method could be as simple as:
MethodInfo GetMethodInfo(Delegate d)
{
return d.Method;
}
You cannot convert directly from a method group to Delegate. But you can use a cast for that. E.g.:
GetMethodInfo((Action)Console.WriteLine)
Be aware that this won't work if you try to mix it with something like usr's solution. For example
GetMethodInfo((Action)(() => Console.WriteLine()))
will return the MethodInfo for the generated anonymous method, not for Console.WriteLine().

This is not possible in C# directly. But you can build this yourself:
static MemberInfo MemberInfoCore(Expression body, ParameterExpression param)
{
if (body.NodeType == ExpressionType.MemberAccess)
{
var bodyMemberAccess = (MemberExpression)body;
return bodyMemberAccess.Member;
}
else if (body.NodeType == ExpressionType.Call)
{
var bodyMemberAccess = (MethodCallExpression)body;
return bodyMemberAccess.Method;
}
else throw new NotSupportedException();
}
public static MemberInfo MemberInfo<T1>(Expression<Func<T1>> memberSelectionExpression)
{
if (memberSelectionExpression == null) throw new ArgumentNullException("memberSelectionExpression");
return MemberInfoCore(memberSelectionExpression.Body, null/*param*/);
}
And use it like this:
var methName = MemberInfo(() => SomeMethod()).MethodName;
That will provide you compile-time safety. Performance will not be good though.

Related

MassTransit. Exclude message types from topology by type object [duplicate]

This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 9 years ago.
I have the following code:
public class ClassExample
{
void DoSomthing<T>(string name, T value)
{
SendToDatabase(name, value);
}
public class ParameterType
{
public readonly string Name;
public readonly Type DisplayType;
public readonly string Value;
public ParameterType(string name, Type type, string value)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException("name");
if (type == null)
throw new ArgumentNullException("type");
this.Name = name;
this.DisplayType = type;
this.Value = value;
}
}
public void GetTypes()
{
List<ParameterType> l = report.GetParameterTypes();
foreach (ParameterType p in l)
{
DoSomthing<p.DisplayType>(p.Name, (p.DisplayType)p.Value);
}
}
}
Now, I know I cannot perform DoSomething()
is there any other way to use this function?
You can, but it involves reflection, but you can do it.
typeof(ClassExample)
.GetMethod("DoSomething")
.MakeGenericMethod(p.DisplayType)
.Invoke(this, new object[] { p.Name, p.Value });
This will look at the top of the containing class, get the method info, create a generic method with the appropriate type, then you can call Invoke on it.
this.GetType().GetMethod("DoSomething").MakeGenericMethod(p.Value.GetType()).Invoke(this, new object[]{p.Name, p.Value});
Should work.
Generics types cannot be specified at runtime the way you'd like to here.
The simplest options would be to add a non-generic overload of DoSomething, or simply call DoSomething<object> and ignore p.DisplayType. Unless SendToDatabase depends on the compile-time type of value (and it probably shouldn't), there should be nothing wrong with giving it an object.
If you can't do those, you'll have to call DoSomething using reflection, and you'll take a big performance hit.
First we need to convert p.Value to the right type, since even if we know the type at compile time we can't pass the string straight to the method...
DoSomething<Int32>( "10" ); // Build error
For simple numeric types and DateTime, we can use
object convertedValue = Convert.ChangeType(p.Value, p.DisplayType);
Now we can use reflection to invoke the required generic method...
typeof(ClassExample)
.GetMethod("DoSomething")
.MakeGenericMethod(p.DisplayType)
.Invoke(this, new object[] { p.Name, convertedValue });
Strictly saying you can use MethodInfo.MakeGenericMethod for this.
But I recommend to change DoSomething to non-generic form instead, since it is not evident whether it really should be generic.

Get the type excluding the standard [duplicate]

This question already has answers here:
How to get the type of T from a member of a generic class or method
(17 answers)
Closed 2 years ago.
I have method like this:
public async Task<IEnumerable<DataResponse>> GetAsync() { ... }
I use reflection and I want to get the type "DataResponse", but I get type "Task<IEnumerable>"
static void Main(string[] args)
{
var assemblies = GetAssemblies();
IEnumerable<Type> controllers =
assemblies.SelectMany(a => a.GetTypes().Where(t => t.IsSubclassOf(typeof(ControllerBase))));
foreach (var controller in controllers)
{
var methods = controller.GetMethods();
foreach (var methodInfo in methods)
{
var returnType = methodInfo.ReturnType;
}
}
}
How do I get types excluding standard types ("task", "IEnumerable", e.t.c)?
Task<IEnumerable> is a generic type whose one generic argument IEnumerable which is in turn a generic type whose generic argument is DataResponse
So methodInfo.ReturnType.GetGenericTypeArguments()[0].GetGenericTypeArguments()[0] would do the trick for this particular method.
Of course this wouldn't solve all cases.
A more generic way
// Pass all types you want to strip here, for example, List<>, IList<>, Task<>, etc.
returnType = StripTypes(returnType, typeof(Task<>), typeof(IEnumerable<>), typeof(List<>));
static Type StripTypes(Type type, params Type[] typesToStrip)
{
if (!type.IsGenericType)
{
return type;
}
var definition = type.GetGenericTypeDefinition();
if (Array.IndexOf(typesToStrip, definition) >= 0)
{
return StripTypes(type.GetGenericArguments()[0], typesToStrip);
}
return type;
}

Use variable as Type [duplicate]

This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 6 years ago.
Is it possible to make such code work?:
private List<Type> Models = new List<Type>()
{
typeof(LineModel), typeof(LineDirectionModel), typeof(BusStopTimeModel), typeof(BusStopNameModel)
};
foreach (Type model in Models) // in code of my method
{
Connection.CreateTable<model>(); // error: 'model' is a variable but is used like a type
}
Thanks in advance
You won't be able to use the variable as a generic type using the conventional syntax (CreateTable<model>). Without knowing what CreateTable does, you have two options:
Instead of making CreateTable a generic method, have it take the type as a parameter:
public static void CreateTable(Type modelType)
{
}
Use Reflection to dynamically invoke the generic method using the desired type:
var methodInfo = typeof (Connection).GetMethod("CreateTable");
foreach (Type model in Models)
{
var genericMethod = methodInfo.MakeGenericMethod(model);
genericMethod.Invoke(null, null); // If the method is static OR
// genericMethod.Invoke(instanceOfConnection, null); if it's not static
}
Note that the reflection way would be slower since the method info won't be resolved until runtime.
You can do it like this,
private List<Type> Models = new List<Type>()
{
typeof(LineModel), typeof(LineDirectionModel), typeof(BusStopTimeModel), typeof(BusStopNameModel)
};
void SomeMethod()
{
MethodInfo genericFunction =Connection.GetType().GetMethod("CreateTable");
foreach (Type model in Models)
{
MethodInfo realFunction = genericFunction.MakeGenericMethod(model);
var ret = realFunction.Invoke(Connection, new object[] { });
}
}

How do I determine if a method is a generic instance of a generic method

I have a MethodInfo passed in to a function and I want to do the following
MethodInfo containsMethod = typeof(ICollection<>).GetMethod("Contains");
if (methodInfo.Equals(containsMethod)
{
// do something
}
But this doesn't work because the methodInfo has a specific generic type. For the example does work if I knew that the ICollection was always of type string.
MethodInfo containsMethod = typeof(ICollection<string>).GetMethod("Contains");
if (methodInfo.Equals(containsMethod)
{
// do something
}
How can I check whether the MethodInfo is a ANY typed instance of the generic method without caring what the type is?
Thanks.
EDIT: Question clarification
As correctly pointed out the method is not generic but the containing class is so the question is more how to I find out if the MethodInfo is for a Type which is a typed instance of ICollection<>.
EDIT: more context
I am writing a Linq provider and trying to handle the "in" case
IList<string> myList = new List<string>{ "1", "2" };
from Something s in ...
where myList.Contains(s.name)
select s;
Note that ICollection<T>.Contains is not a generic method - it is a non-generic method of a generic type. Otherwise IsGenericMethod and GetGenericTypeDefinition would help. You could obtain the generic type definition (DeclaringType.GetGenericTypeDefinition()) and work back up to Contains, but I wonder if you are approaching this problem the hard way.
Usually, if you are using reflection, it may be pragmatic to drop to non-generic IList - unless you need the type data (for example, for meta-programming). And in that case, I would consider looking closely to see if you can simplify the setup here.
You could check the declaring type:
if( methodInfo.Name == "Contains"
&& methodInfo.DeclaringType.IsGenericType
&& methodInfo.DeclaringType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
Some error checking would need to be added to this, but I believe this roughly does what you want. You can use a method with or without a type argument as the parameter.
static bool IsContainsMethod(MethodInfo methodInfo)
{
Type[] types = { methodInfo.GetParameters().First().ParameterType };
MethodInfo containsMethod = typeof(ICollection<>).MakeGenericType(types).GetMethod("Contains");
return methodInfo.Equals(containsMethod);
}
The problem is that you don't have a generic method: you have a non-generic method on a generic type. I don't know of a way to use reflection to go directly from a method definition on an open generic type to that same method on a closed generic type or vice versa. However, you can take advantage of the fact that the methods returned by GetMethods() on the open and closed generic types should always be in the same order and do the translation by index:
MethodInfo containsMethod = typeof(ICollection<>).GetMethod("Contains");
var methodIndex = Array.IndexOf(methodInfo.DeclaringType.GetMethods(), methodInfo);
var methodOnTypeDefinition = methodInfo.DeclaringType.GetGenericTypeDefinition().GetMethods()[methodIndex];
if (methodOnTypeDefinition.Equals(containsMethod))
{
// do something
}
try this method
public static bool CheckGenericMethod(MethodInfo methodInfo)
{
bool areSimilarMethods = false;
MethodInfo methodToCompare = typeof(ISomeInterface<>).GetMethod("func");
Type interfaceInfo = methodInfo.DeclaringType.GetInterface(methodToCompare.DeclaringType.FullName);
if (interfaceInfo != null)
areSimilarMethods = (methodToCompare.Name.Equals(methodInfo.Name)
&& interfaceInfo.FullName.Contains(methodToCompare.DeclaringType.FullName));
else
{
areSimilarMethods = methodToCompare.DeclaringType.Equals(methodInfo.DeclaringType);
}
return areSimilarMethods;
}
and here is the full example usage.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace TestReflection
{
public class Program
{
static void Main(string[] args)
{
MethodInfo info1 = typeof(ISomeInterface<>).GetMethod("func");
MethodInfo info2 = typeof(MyStringCollection).GetMethod("func");
MethodInfo info3 = typeof(MyProgramCollection).GetMethod("func");
MethodInfo info4 = typeof(MyXCollection).GetMethod("func");
if (CheckGenericMethod(info1)) Console.WriteLine("true");else Console.WriteLine("false");
if (CheckGenericMethod(info2)) Console.WriteLine("true");else Console.WriteLine("false");
if (CheckGenericMethod(info3)) Console.WriteLine("true");else Console.WriteLine("false");
if (CheckGenericMethod(info4)) Console.WriteLine("true"); else Console.WriteLine("false");
Console.ReadKey();
}
public static bool CheckGenericMethod(MethodInfo methodInfo)
{
bool areSimilarMethods = false;
MethodInfo methodToCompare = typeof(ISomeInterface<>).GetMethod("func");
Type interfaceInfo = methodInfo.DeclaringType.GetInterface(methodToCompare.DeclaringType.FullName);
if (interfaceInfo != null)
areSimilarMethods = (methodToCompare.Name.Equals(methodInfo.Name)
&& interfaceInfo.FullName.Contains(methodToCompare.DeclaringType.FullName));
else
{
areSimilarMethods = methodToCompare.DeclaringType.Equals(methodInfo.DeclaringType);
}
return areSimilarMethods;
}
}
public interface ISomeInterface<T> where T : class
{
T func(T s);
}
public class MyStringCollection : ISomeInterface<string>
{
public string func(string s)
{
return s;
}
}
public class MyProgramCollection : ISomeInterface<Program>
{
public Program func(Program s)
{
return s;
}
}
public class MyXCollection
{
public int func(int s)
{
return s;
}
}
}

Generic Method Executed with a runtime type [duplicate]

This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 9 years ago.
I have the following code:
public class ClassExample
{
void DoSomthing<T>(string name, T value)
{
SendToDatabase(name, value);
}
public class ParameterType
{
public readonly string Name;
public readonly Type DisplayType;
public readonly string Value;
public ParameterType(string name, Type type, string value)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException("name");
if (type == null)
throw new ArgumentNullException("type");
this.Name = name;
this.DisplayType = type;
this.Value = value;
}
}
public void GetTypes()
{
List<ParameterType> l = report.GetParameterTypes();
foreach (ParameterType p in l)
{
DoSomthing<p.DisplayType>(p.Name, (p.DisplayType)p.Value);
}
}
}
Now, I know I cannot perform DoSomething()
is there any other way to use this function?
You can, but it involves reflection, but you can do it.
typeof(ClassExample)
.GetMethod("DoSomething")
.MakeGenericMethod(p.DisplayType)
.Invoke(this, new object[] { p.Name, p.Value });
This will look at the top of the containing class, get the method info, create a generic method with the appropriate type, then you can call Invoke on it.
this.GetType().GetMethod("DoSomething").MakeGenericMethod(p.Value.GetType()).Invoke(this, new object[]{p.Name, p.Value});
Should work.
Generics types cannot be specified at runtime the way you'd like to here.
The simplest options would be to add a non-generic overload of DoSomething, or simply call DoSomething<object> and ignore p.DisplayType. Unless SendToDatabase depends on the compile-time type of value (and it probably shouldn't), there should be nothing wrong with giving it an object.
If you can't do those, you'll have to call DoSomething using reflection, and you'll take a big performance hit.
First we need to convert p.Value to the right type, since even if we know the type at compile time we can't pass the string straight to the method...
DoSomething<Int32>( "10" ); // Build error
For simple numeric types and DateTime, we can use
object convertedValue = Convert.ChangeType(p.Value, p.DisplayType);
Now we can use reflection to invoke the required generic method...
typeof(ClassExample)
.GetMethod("DoSomething")
.MakeGenericMethod(p.DisplayType)
.Invoke(this, new object[] { p.Name, convertedValue });
Strictly saying you can use MethodInfo.MakeGenericMethod for this.
But I recommend to change DoSomething to non-generic form instead, since it is not evident whether it really should be generic.

Categories