I would like to specify a parameter that can accept a method without having to specify generic arguments to produce the MethodInfo of the given method.
For example, I'd like to write code like this:
interface IService
{
object AMethod(int p1, int p2);
}
IThingy<IService>() thingy;
thingy.Add(svc => svc.AMethod);
The closest options I can produce are:
interface IThingy<TService>
{
void Add1<T0, T1, TResult>(Expression<Func<TService, Func<T0, T1, TResult>>> expression);
void Add2(Expression<Func<TService, Func<int, int, object>>> expression);
void Add3(Expression<Action<TService>> expression);
}
thingy.Add1<int, int, object>(svc => svc.AMethod);
thingy.Add2(svc => svc.AMethod);
thingy.Add3(svc => svc.AMethod(0, 0));
Add1 implies many Func overloads, which I'm ok with, but cannot be called without specifying the generic parameters.
Add2 requires no generic parameters, but implies a specific overload for every parameter signature.
Add3 requires a call to the method, including fake parameters.
FYI, I will process the expression to get the MethodInfo of the given method like this:
MemberInfo GetMemberFromExpression<T>(Expression<ActionT>> expression)
{
return ((MethodCallExpression)expression.Body).Method
}
GetMemberFromExpression(svc => svc.AMethod(0, 0));
Passing methods to expressions with their invoked values
You can use the GetMemberFromExpression method as you have it, and then simply remove the Generic parameter. As follows:
static void Main(string[] args)
{
var memberInfo1 = GetMemberFromExpression(() => Method1(10, 20));
var memberInfo2 = GetMemberFromExpression(() => Method2());
var memberInfo3 = GetMemberFromExpression(() => Method3("string", 15, DateTime.Now));
Console.WriteLine(memberInfo1.Name);
Console.WriteLine(memberInfo2.Name);
Console.WriteLine(memberInfo3.Name);
Console.Read();
}
public static MemberInfo GetMemberFromExpression(Expression<Action> expression)
{
return ((MethodCallExpression)expression.Body).Method;
}
public static object Method1(int p1, int p2)
{
return p1 + p2;
}
public static void Method2()
{
// No return
}
public static double Method3(string p1, int p2, DateTime p3)
{
return 10d;
}
You'll see that the GetMemberFromExpression will return MethodInfo of any method you pass it, regardless or parameter type and return type.
Ignoring overloads, invoking on instance and name
If you're not concerned about overloads, you could probably use simple reflection instead of building up Expressions. The nameof operator in c# 6 would be a better approach than passing in the name as a string (compile-time checking).
public static MemberInfo GetMemberInfo(Type type, string methodName)
{
return type.GetMethod(methodName);
}
Note, theres no validation checks in this method, just to show the concept.
The above method will work for any instance or static methods. Simply pass the instance type / or static class type and the method name as follows:
MyClass cl = new MyClass();
var methodInfo1 = GetMemberInfo(cl.GetType(), "AMethod");
var methodInfo2 = GetMemberInfo(typeof(MyClass), "AStaticMethod");
Here's MyClass with the methods:
class MyClass
{
public void AMethod(int a, int b)
{
// instance method
}
public static bool AStaticMethod(bool a, bool b)
{
return a & b; // static method
}
}
This way you don't pass any parameters because you're simply investigating the definition and not invoking.
Using Expressions with types rather than values
Here's a third option. This way you have:
Compile-time checking,
No need to pass valued ("fake") parameters,
Only the parameter types required (which ensures overloaded methods will work).
Create a class catering for the Action and Func overloads:
public class Method
{
public static MethodInfo GetInfo<TReturn>(Func<TReturn> method)
{
return method.Method;
}
public static MethodInfo GetInfo<TP1, TReturn>(Func<TP1, TReturn> method)
{
return method.Method;
}
public static MethodInfo GetInfo<TP1, TP2, TReturn>(Func<TP1, TP2, TReturn> method)
{
return method.Method;
}
//... Continue with some more Func overloads
public static MethodInfo GetInfo(Action method)
{
return method.Method;
}
public static MethodInfo GetInfo<TP1>(Action<TP1> method)
{
return method.Method;
}
public static MethodInfo GetInfo<TP1, TP2>(Action<TP1, TP2> method)
{
return method.Method;
}
//... Continue with some more Action overloads
}
Now you can simply get your MethodInfo as follows:
var methodInfo1 = Method.GetInfo<int, int>(cl.AMethod);
var methodInfo2 = Method.GetInfo<bool, bool, bool>(MyClass.AStaticMethod);
Yes you'll have to create a bunch of overloads in the Method class for Action and Func, but that's a once off thing you'll do and .NET does the same with the Action and Func delegates to cater for all the overloads.
Related
I'm trying to call a generic method at run time, and have working code. However, I was wondering if there was a better way of getting the method info, as should i change the method name, it will break.
public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
//Create code here...
}
//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
MethodInfo method = this.GetType().GetMethod("Handle");
//MethodInfo method = this.GetType().GetMethods()
.Where(mi => mi.IsGenericMethod && mi.Name == "Handle").First();
MethodInfo genericMethod = method.MakeGenericMethod(new Type[] { codeType });
return (int)genericMethod.Invoke(this, new object[] { obj });
}
I had hoped there was a nicer way of doing this, perhaps using actions to get the method info, but then I still need to provide a type, e.g.
Action<CreateCodeModel> h = (x) => Handle(x);
MethodInfo method = h.Method;
You are on the right track here. Your delegate signature must match the method you are calling, so in this case that would be Func<CreateCodeModel,int> instead of Action<CreateCodeModel>. And you must supply a generic parameter that matches the generic constraints. This could be anything since we'll just remove it with our call to 'GetGenericMethodDefinition', but I like to use the class from the constraint.
The other piece of the code below is the idea that you only need to get the generic method definition once, as it doesn't change, so I store that in a static variable so it can be reused.
public int Handle<T>(CreateCodeModel obj) where T : CodeModel
{
//Create code here...
return 7;
}
// this static variable preserves the generic MethodInfo, so we don't have
// keep discovering it with reflection
private static MethodInfo _methodInfoForHandle;
// The generic MethodInfo only needs to be discovered the first time this runs
private MethodInfo MethodInfoForHandle
{
get
{
return _methodInfoForHandle ?? (_methodInfoForHandle = GetMethodInfoForHandleMethod());
}
}
private MethodInfo GetMethodInfoForHandleMethod()
{
Func<CreateCodeModel, int> handleFunc = Handle<CodeModel>;
return handleFunc.Method.GetGenericMethodDefinition();
}
//codeType is selected by user.
public int Handle(CreateCodeModel obj, Type codeType)
{
MethodInfo genericMethod = MethodInfoForHandle.MakeGenericMethod(new Type[] { codeType });
return (int)genericMethod.Invoke(this, new object[] { obj });
}
public class CreateCodeModel { }
public class CodeModel { }
public class JavascriptCodeModel : CodeModel { }
I have the following method where T is used inside a Func:
public void DoSomething<T>(string someString, Func<T, bool> someMethod)
{
if(someCondition)
{
string A;
bool resultA = someMethod(A);
}
else
{
string[] B;
bool resultB = someMethod(B);
}
// Some other stuff here ...
}
I am invoking the DoSomething method in the following manner:
DoSomething<string>("abc", someMethod);
DoSomething<string[]>("abc", someMethod);
And the someMethod exists with the following definitions:
bool someMethod(string simpleString);
bool someMethod(string[] stringArray);
Now the compilation fails with the following errors in method DoSomething:
cannot convert from 'string' to 'T'
cannot convert from 'string[]' to 'T'
I am unable to figure out if there is a solution to the problem, or what I am trying is not feasible. It looks similar to question How can I pass in a func with a generic type parameter?, though it was not helpful for my scenario.
Your example seems a little inconsistent, but if you were writing things generically, it should look more like this:
public void DoSomething<T>(string someString, Func<T, bool> someMethod)
{
T a;
someMethod(a);
}
Notice that instead of using if to choose between types, and then declaring the type as either a string or string[], we simply declare the type as T, which will get substituted when the code is compiled so that it will be appropriate for the function.
The moment you find yourself picking between types using if or switch case, you probably don't want a generic solution; the logic isn't, in fact, generic at all. It is specific. In that sort of case, just write two prototypes:
public void DoSomething(string someString, Func<string, bool> someMethod)
{
string A;
bool resultA = someMethod(A);
}
public void DoSomething(string someString, Func<string[], bool> someMethod)
{
string[] A;
bool resultA = someMethod(A);
}
This is known as method overloading. The compiler will automatically pick the right method with the right arguments by inferring the types from the supplied function.
You can achieve it via reflection:
public void DoSomething<T>(string someString, Func<T, bool> someMethod)
{
var args = new Dictionary<Type, object>
{
[typeof(string)] = "string", //string A;
[typeof(string[])] = new[] { "string" }, //string[] B;
};
var arg = args[typeof(T)];
var result = (bool)someMethod.Method.Invoke(someMethod.Target, new[] { arg });
}
Usage:
DoSomething<string>("abc", someMethod);
DoSomething<string[]>("abc", someMethod);
Is there a way to pass a method name in a generic manner, without passing its parameters, so it can be invoked by the method, with passed arguments?
Consider this example:
public class Client
{
public string Convert(int value)
{
return value.ToString();
}
}
public class Wrapper<TClient>
{
TClient Client;
public TResult Invoke<TArg, TResult>(Func<TClient, TArg, TResult> action, TArg arg)
{
return action(Client, arg);
}
}
I want to be able to pass to the wrapper the method of TClient I want to invoke, and pass the actual arguments along, all generically:
var wrapper = new Wrapper<Client>();
wrapper.Invoke(c => c.Convert, 5);
Is there any possible way to achieve that, without hard coding the method name, or losing its genericness (i.e. by using Delegate)?
Notes:
The Client is an external sealed class that exposes a gazillion methods each of many parameters. I want wrap its behavior and I don't mind writing all the necessary code in the wrapper, but the usage of the wrapper should be as clean as possible.
Update
I want to avoid the need to specify the parameters. The whole idea is having them inferred from the specified action.
You're very close to getting your code to run. There are two options.
First, you can try this:
public class Wrapper<TClient>
{
public TResult Invoke<TArg, TResult>(Func<TArg, TResult> action, TArg arg)
{
return action(arg);
}
}
Then call it like this:
var wrapper = new Wrapper<Client>();
wrapper.Invoke(wrapper.client.Convert, 5);
Or, alternatively, you can do this:
public class Wrapper<TClient>
{
public Wrapper(TClient client)
{
this.Client = client;
}
private TClient Client;
public TResult Invoke<TArg, TResult>(Func<TClient, TArg, TResult> action, TArg arg)
{
if (operation.Target != Client)
throw new ArgumentException(nameof(operation));
return action(this.Client, arg);
}
}
And call it like this:
var client = new Client();
var wrapper = new Wrapper<Client>(client);
wrapper.Invoke((c, a) => c.Convert(a), 5);
But, from your description of your problem, I don't see how either of these help and I don't see how to implement what you're asking. Perhaps you need to provide more detail as to what the underlying need you're trying to solve?
You want the expression being passed to Invoke to return a Func that accepts TArg. In code:
public class Wrapper<TClient>
{
TClient Client;
public TResult Invoke<TArg, TResult>(Func<TClient, Func<TArg, TResult>> action, TArg arg)
{
return action(Client)(arg);
}
}
You can then invoke it like so:
class Program
{
static void Main(string[] args)
{
var wrapper = new Wrapper<Client>();
string result = wrapper.Invoke<int, string>(c => c.Convert, 5);
}
}
Since you don't like the approach of having to explicitly specify type arguments, you can use a slightly different API (which comes with its own annoyances):
public class Wrapper<TClient>
{
TClient Client;
public void Invoke<TArg, TResult>(Func<TClient, Func<TArg, TResult>> action, TArg arg, out TResult result)
{
return action(Client)(arg);
}
}
You can call this like so, with the return type inferred from the out parameter:
class Program
{
static void Main(string[] args)
{
var wrapper = new Wrapper<Client>();
string result;
wrapper.Invoke(c => c.Convert, 5, out result);
}
}
I have a generic class with a static method, that method using the type parameter:
GenericClass<T>
{
public static void Method()
{
//takes info from typeof(T)
}
}
Now, I need to access that static method, but not simply using GenericClass<KnownType>.Method(). I need to do this having a Type instance. So:
public void OutsiderMethod(Type T)
{
GenericClass<T>.Method()
//it's clear this line won't compile, for T is a Type instance
//but i want some way to have access to that static method.
}
Using reflections, I probably can get a way to invoke that method by it's string name, using some MethodInfo stuff.
That is partially good, solves the question.
But if possible, I'd love to have not to use the name as string.
Anyone???
Generic methods of non-generic classes are easier to access than non-generic methods of generic classes.
You can create a helper method that simply calls the real method:
void OutsiderMethodHelper<T>()
{
GenericClass<T>.Method();
}
You can then get the MethodInfo for that method without looking it up by name-as-string:
public void OutsiderMethod(Type T)
{
Action action = OutsiderMethodHelper<object>;
action.Method.GetGenericMethodDefinition().MakeGenericMethod(T).Invoke(null, null);
}
Here is an example using the Expression:
public static class GenericHelper
{
public static object Invoke(Expression<Action> invokeMethod, object target, Type genericType, params object[] parameters)
{
MethodInfo methodInfo = ParseMethodExpression(invokeMethod);
if (!methodInfo.DeclaringType.IsGenericType)
throw new ArgumentException("The method supports only generic types");
Type type = methodInfo.DeclaringType.GetGenericTypeDefinition().MakeGenericType(genericType);
MethodInfo method = type.GetMethod(methodInfo.Name);
return method.Invoke(target, parameters);
}
public static object Invoke(Expression<Action> invokeMethod, Type genericType, params object[] parameters)
{
return Invoke(invokeMethod, null, genericType, parameters: parameters);
}
private static MethodInfo ParseMethodExpression(LambdaExpression expression)
{
Validate.ArgumentNotNull(expression, "expression");
// Get the last element of the include path
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null)
{
var memberExpression = unaryExpression.Operand as MethodCallExpression;
if (memberExpression != null)
return memberExpression.Method;
}
var expressionBody = expression.Body as MethodCallExpression;
if (expressionBody != null)
return expressionBody.Method;
throw new NotSupportedException("Expession not supported");
}
}
The method call will be look like this:
GenericHelper.Invoke(() => GenericClass<object>.Method(), typeof(string));
I want to be able to do this:
var test = SomeMethod(s => s.SomeMethod);
I can make it work with properties by making the method signature look like this:
SomeMethod<TProperty>(Expression<Func<T, TProperty>> expression)
How can I make it work with methods? I know this is simple, I'm just missing something small.
Moq approaches this by requiring the method parameters to be stubbed out with special 'marker' objects like:
test.Setup(m => m.SomeMethod(It.IsAny<int>()));
Incidentally, this allows Moq to resolve method overloads (a single method name is ambiguous if you don't have any notion of the required parameters.) They are going a bit further and using it to actually match parameters based on criteria, for the purpose of mocking objects for unit testing. The source code is available and might give you some ideas. They do all sorts of fun trickery with expression trees.
Here is an example based on the Moq source (hacked out a bit, but shows how the method can be extracted from the expression):
internal static void ExtractMethod<T>(Expression<Action<T>> expression)
where T : class
{
var methodCall = expression.ToMethodCall();
var method = methodCall.Method;
var args = methodCall.Arguments.ToArray();
}
Straight from Moq source:
/// <summary>
/// Casts the body of the lambda expression to a <see cref="MethodCallExpression"/>.
/// </summary>
/// <exception cref="ArgumentException">If the body is not a method call.</exception>
public static MethodCallExpression ToMethodCall(this LambdaExpression expression)
{
Guard.NotNull(() => expression, expression);
var methodCall = expression.Body as MethodCallExpression;
if (methodCall == null)
{
throw new ArgumentException(string.Format(
CultureInfo.CurrentCulture,
Resources.SetupNotMethod,
expression.ToStringFixed()));
}
return methodCall;
}
This will work with your code, but you'd have to pass 'dummy' parameters to allow the compiler to create the expression. So if you had:
public void SomeMethod(int value, string text) {}
Then you'd pass it as:
ExtractMethod(s => s.SomeMethod(0, null));
Is something like this what you're looking for?
DoSomethingWithAction<T>(Func<T, Action> actionRetriever) { }
DoSomethingWithFunction<T, TResult>(Func<T, Func<TResult>> functionRetriever) { }
You would call these like:
DoSomethingWithAction<ObjectWithMethod>(obj => obj.Method);
DoSomethingWithFunction<ObjectWithProperty>(obj => obj.Property);
Something like this?
public SimpleCommand( Predicate<object> canExecuteDelegate, Action<object> executeDelegate )
{
CanExecuteDelegate = canExecuteDelegate;
ExecuteDelegate = executeDelegate;
}
You will need to specify the function signature using Predicate or Action.
class Program
{
public class Test
{
public bool SomeMethod(string test)
{
return true;
}
}
static void Main(string[] args)
{
Test testObj = new Test();
Func<string, bool> rule1 = AddRule(testObj, x => x.SomeMethod);
bool rsult = rule1("ttt");
}
static Func<string, bool> AddRule<T>( T obj, Func<T,Func<string, bool>> func)
{
return func(obj);
}
}
I think, there's no way of doing this without specifying the method signature: Just think of overloaded methods:
void SomeMethod() { }
int SomeMethod(int a, int b) { return 0; }
// ...
var test = TestMethod(s => s.SomeMethod);
How would the compiler possibly know, which of the methods you want to test?