Converting instance of Interface with generic method to Action - c#

I'm struggling to create instances of Action<> using the MethodInfo's I can retrieve using reflection.
Using the example below I can easily call a custom method on my objects, but I would really like to convert them to Actions.
I have tried doing it by using Delegate.CreateDelegate but I can't seem to get this to return an Action with a generic type.
Example
I have created the following interface:
interface IMyTest { } // Marker interface
interface<TInput> IMyTest : IMyTest {
void MyMethod(TInput input);
}
Then I inherit the interface in a class:
class MyClass : IMyTest<DateTime>, IMyTest<int> {
void MyMethod(DateTime input) {
// Do something
}
void MyMethod(int input) {
// Do something else
}
}
And then I create a method that uses reflection to find and call "MyMethod" from the interface instance:
void DoCallMyMethod(object target, object input) {
IEnumerable<Type> interfaces = target.GetType().GetInterfaces()
.Where(x => typeof(IMyTest).IsAssignableFrom(x) && x.IsGenericType);
foreach (Type #interface in interfaces) {
Type type = #interface.GetGenericArguments()[0];
MethodInfo method = #interface.GetMethod("MyMethod", new Type[] { type });
if (method != null) {
method.Invoke(target, new[] { input });
}
}
}
And finally I put it all together:
MyClass foo = new MyClass();
DoCallMyMethod(foo, DateTime.Now);
DoCallMyMethod(foo, 47);
What I would want
Inside DoCallMyMethod I would like MethodInfo method to be converted to a generic Action so that the result would be something like this:
Action<type> myAction = method;
But this obviously doesn't work.
I found some similar SO posts (but none that cover exactly my case) that ended up with an answer similar to this:
Action<object> action =
(Action<object>)Delegate.CreateDelegate(typeof(Action<object>), target, method);
But this doesn't work because "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."
How can I get an Action with the specified type as its input type, while still retaining the object references (without new instances appearing)?

You can't create an Action<object> from one of MyMethods because the method is expecting a specific object's type (int, DateTime) and if you treat it as an Action<object> you might invoke it with objects of any type.
Since what you need is return a Action<T> there is not need to pass the input value to DoCallMyMethod method. You can pass the input type as a generic parameter:
public Action<T> DoCallMyMethod<T>(object target)
{
var #interface = typeof(IMyTest<T>);
if (#interface.IsAssignableFrom(target.GetType()))
{
var method = #interface.GetMethod("MyMethod");
if (method != null)
{
var action = Delegate.CreateDelegate(typeof(Action<T>), target, method) as Action<T>;
return action;
}
}
return null;
}
Then, use it like this:
MyClass foo = new MyClass();
var action1 = DoCallMyMethod<DateTime>(foo);
var action2 = DoCallMyMethod<int>(foo);
action1(DateTime.Now);
action2(47);
If you don't know at compile time the type of the input you can try this:
public Delegate DoCallMyMethod(object target, Type inputType)
{
var #interface = typeof(IMyTest<>).MakeGenericType(inputType);
if (#interface.IsAssignableFrom(target.GetType()))
{
var method = #interface.GetMethod("MyMethod");
if (method != null)
{
var #delegate = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(inputType), target, method);
return #delegate;
}
}
return null;
}
And use it like so:
MyClass foo = new MyClass();
var action1 = DoCallMyMethod(foo, typeof(DateTime)) as Action<DateTime>;
var action2 = DoCallMyMethod(foo, typeof(int)) as Action<int>;
action1(DateTime.Now);
action2(47);
//or
int input = 47;
var #delegate = DoCallMyMethod(foo, input.GetType());
#delegate.DynamicInvoke(input);
But if you need to returns an Action<object> you can make an action receiving an object and calling the method if the object's type is valid:
public Action<object> DoCallMyMethod(object target, Type inputType)
{
var #interface = typeof(IMyTest<>).MakeGenericType(inputType);
if (#interface.IsAssignableFrom(target.GetType()))
{
var method = #interface.GetMethod("MyMethod");
if (method != null)
{
Action<object> action = obj =>
{
if (obj.GetType().IsAssignableFrom(inputType))
method.Invoke(target, new object[] { obj });
};
return action;
}
}
return null;
}
And...
MyClass foo = new MyClass();
Action<object> action = DoCallMyMethod(foo, typeof(int));
action(47); // MyMethod(int) is called.
action("..."); // Nothing happens.
Now you have an Action<object> that calls MyMethod(int) only when an integer is passed.

Related

How to get MethodInfo for a generic method by name and its generic parameters?

I have this interface:
interface IRepository
{
string GetId<T>(T obj) where T : IDomainObject;
string GetId<T>(Reference<T> reference) where T : IDomainObject;
}
What exactly IDomainObject and Reference are is not relevant to this question. So assume they are completely empty:
interface IDomainObject
{
// something
}
class Reference<T> where T : IDomainObject
{
// something
}
My question is: How do I get the MethodInfo for the GetId<T> method in IRepository that accepts a Reference<T>?
Here's what I've tried:
public MethodInfo GetReferenceAcceptingGetIdMethod()
{
// We want to return the MethodInfo for the GetId<T> method of IRepository
// that accepts a Reference<T> argument.
var repositoryInterfaceType = typeof(IRepository);
// Look through all methods of IRepository...
foreach (var m in repositoryInterfaceType.GetMethods())
{
// ... to find a candidate method, going by Genericness and Name ...
if (m.IsGenericMethodDefinition && m.Name == nameof(IRepository.GetId))
{
// ... and to narrow it further down by looking at the parameters ...
var parameters = m.GetParameters();
if (parameters.Length == 1)
{
// ... to check if the one and only parameter is a generic Reference<>.
var firstParamType = parameters[0].ParameterType;
var genericReferenceType = typeof(Reference<>);
if (firstParamType == genericReferenceType)
{
// !!! This if will never be true.
// Why?
// And what do I have to change the condition into to make it work?
return m;
}
}
}
}
throw new Exception();
}
It appears that method's parameter type is somehow different from a completely open generic type. I guess and it seems that the type of the method's parameter is somehow linked to the method's generic type parameter.
So how can I get the MethodInfo in such a case?
The parameter type can't be the generic type definition. It has a type argument - which is the type parameter for the method. You can get that type parameter from MethodInfo.GetGenericArguments(), and then use it with typeof(Reference<>).MakeGenericType(...) to get the expected parameter type.
Here's a complete example, basically adapting your original code:
using System;
using System.Reflection;
class Reference<T> {}
interface IDomainObject {}
interface IRepository
{
string GetId<T>(T obj) where T : IDomainObject;
string GetId<T>(Reference<T> reference) where T : IDomainObject;
}
class Test
{
static void Main()
{
var method = GetReferenceAcceptingGetIdMethod();
Console.WriteLine(method);
}
public static MethodInfo GetReferenceAcceptingGetIdMethod()
{
var repositoryInterfaceType = typeof(IRepository);
foreach (var m in repositoryInterfaceType.GetMethods())
{
if (m.IsGenericMethodDefinition && m.Name == nameof(IRepository.GetId))
{
var typeParameter = m.GetGenericArguments()[0];
var expectedParameterType = typeof(Reference<>).MakeGenericType(typeParameter);
var parameters = m.GetParameters();
if (parameters.Length == 1)
{
var firstParamType = parameters[0].ParameterType;
if (firstParamType == expectedParameterType)
{
return m;
}
}
}
}
throw new Exception();
}
}

Dynamically convert Func to corresponding Action

I'm trying to use the Convert method on functions as well as actions, so I can avoid writing duplicate methods taking in delegates of Func type. Convert method comes from Convert Action<T> to Action<object>
public class Program
{
static void Main(string[] args)
{
var program = new Program();
var mi = program.GetType().GetMethod("Function", BindingFlags.Instance | BindingFlags.Public);
// Can be any version of Func
var funcType = typeof(Func<int, int>);
// Create action delegate somehow instead
var del = mi.CreateDelegate(funcType, null);
// Or dynamically convert the Func to a corresponding Action type (in this case Action<int>)
}
// Or find a way to pass it in as a parameter here
public Action<object> Convert<T>(Action<T> action)
{
return o => action((T)o);
}
public int Function(int five)
{
return five;
}
}
I think you are looking for something like this:
public static Action<T1> IgnoreResult<T1,T2>(Func<T1,T2> func)
{
return x => func(x);
}
But for all variants of Func<T1,T2....>
I think this would work:
public static Action<TR> IgnoreResult<TR>(Delegate f)
{
return x => f.DynamicInvoke(x);
}
With usage:
var action = IgnoreResult<int>(new Func<int,int>(program.Function));
action(5);
You'll not be able to get it to infer the parameters and return type without copy and pasting the first example for all variants of Action<T1...> and Func<T1,T2...>.

Dynamically declare a type for a method out parameter

I am struggling to describe this problem I have, but here it is:
Suppose I now have the type of a property on one member of a class (instance):
Type t = propertyInfo.PropertyType;
How do I declare or setup some variable, in order to receive a method call result later, using the out keyword?
t value; // Obviously doesn't compile, How do I declare this?
// or this?
//var value = default(t); // doesn't work
someObject.GetData(out value);
The premise here is that I don't own someObject and I am stuck with this method call signature.
If there is for example a class:
internal class Test
{
public void GetData(out int value)
{
value = 42;
}
}
The method can be called with Invoke() passing object[] as arguments:
var obj = new Test();
var type = obj.GetType();
var m = type.GetMethod("GetData");
var pars = new object[] { null };
m.Invoke(obj, pars);
I may be misunderstanding something about the complexity of the problem here, but, if you have a compile time instance of someObject, you can wrap the evil up like this:
class OutWrap<T,U> where T : U
{
private readonly SomeObject<T> obj;
public OutWrap(SomeObject<T> obj)
{
this.obj = obj;
}
public T Value
{
get
{
T t;
obj.GetData(out t);
return t;
}
}
}
and use it:
var evil = new SomeObject<int>(); // or however/whereever you get it from
var wrap = new OutWrap<int, int>(evil);
var output = wrap.Value;

How to pass an argument when invoking a method (reflection)?

I need to call a method, passing an int. using the following code I can fetch the method but not passing the argument. How to fix it?
dynamic obj;
obj = Activator.CreateInstance(Type.GetType(String.Format("{0}.{1}", namespaceName, className)));
var method = this.obj.GetType().GetMethod(this.methodName, new Type[] { typeof(int) });
bool isValidated = method.Invoke(this.obj, new object[1]);
public void myMethod(int id)
{
}
The new object[1] part is how you're specifying the arguments - but you're just passing in an array with a single null reference. You want:
int id = ...; // Whatever you want the value to be
object[] args = new object[] { id };
method.Invoke(obj, args);
(See the MethodBase.Invoke documentation for more details.)
Note that method.Invoke returns object, not bool, so your current code wouldn't even compile. You could cast the return value to bool, but in your example that wouldn't help at execution time as myMethod returns void.
Just pass an object with the invoke method
namespace test
{
public class A
{
public int n { get; set; }
public void NumbMethod(int Number)
{
int n = Number;
console.writeline(n);
}
}
}
class MyClass
{
public static int Main()
{
test mytest = new test();
Type myTypeObj = mytest.GetType();
MethodInfo myMethodInfo = myTypeObj.GetMethod("NumbMethod");
object[] parmint = new object[] {5};
myMethodInfo.Invoke(myClassObj, parmint);
}
}

Getting a delegate from methodinfo

I have a drop down list that is populated by inspecting a class's methods and including those that match a specific signature. The problem is in taking the selected item from the list and getting the delegate to call that method in the class. The first method works, but I cannot figure out part of the second.
For example,
public delegate void MyDelegate(MyState state);
public static MyDelegate GetMyDelegateFromString(string methodName)
{
switch (methodName)
{
case "CallMethodOne":
return MyFunctionsClass.CallMethodOne;
case "CallMethodTwo":
return MyFunctionsClass.CallMethodTwo;
default:
return MyFunctionsClass.CallMethodOne;
}
}
public static MyDelegate GetMyDelegateFromStringReflection(string methodName)
{
MyDelegate function = MyFunctionsClass.CallMethodOne;
Type inf = typeof(MyFunctionsClass);
foreach (var method in inf.GetMethods())
{
if (method.Name == methodName)
{
//function = method;
//how do I get the function to call?
}
}
return function;
}
How do I get the commented out section of the second method to work? How do I cast the MethodInfo into the delegate?
Thanks!
Edit: Here is the working solution.
public static MyDelegate GetMyDelegateFromStringReflection(string methodName)
{
MyDelegate function = MyFunctionsClass.CallMethodOne;
Type inf = typeof(MyFunctionsClass);
foreach (var method in inf.GetMethods())
{
if (method.Name == methodName)
{
function = (MyDelegate)Delegate.CreateDelegate(typeof(MyDelegate), method);
}
}
return function;
}
public static Delegate CreateDelegate(this MethodInfo methodInfo, object target) {
Func<Type[], Type> getType;
var isAction = methodInfo.ReturnType.Equals((typeof(void)));
var types = methodInfo.GetParameters().Select(p => p.ParameterType);
if (isAction) {
getType = Expression.GetActionType;
}
else {
getType = Expression.GetFuncType;
types = types.Concat(new[] { methodInfo.ReturnType });
}
if (methodInfo.IsStatic) {
return Delegate.CreateDelegate(getType(types.ToArray()), methodInfo);
}
return Delegate.CreateDelegate(getType(types.ToArray()), target, methodInfo.Name);
}
You'll need to call some form of Delegate.CreateDelegate(), depending on whether the method in question is a static or instance method.
Here is a simpler version of Sagi's solution using Expression.GetDelegateType() instead of manually detecting whether an Action or Func is needed:
public static Delegate CreateDelegate(this MethodInfo methodInfo, object target)
{
var parmTypes = methodInfo.GetParameters().Select(parm => parm.ParameterType);
var parmAndReturnTypes = parmTypes.Append(methodInfo.ReturnType).ToArray();
var delegateType = Expression.GetDelegateType(parmAndReturnTypes);
if (methodInfo.IsStatic)
return methodInfo.CreateDelegate(delegateType);
return methodInfo.CreateDelegate(delegateType, target);
}

Categories