4

Consider a generic method as follow:

class SomeClass
{
    public static void SomeMethod<T>(Func<T>);
}

I would like to call this method using reflection. This is how far I could make it:

_SomeMethod = typeof(SomeClass).GetMethod("SomeMethod",
    BindingFlags.Public | BindingFlags.Static);
Type type = typeof(SomeType); //Actually SomeType is extracted using reflection and it's not fixed
MethodInfo toBeCalled = _SomeMethod.MakeGenericMethod(type);
object obj = Activator.CreateInstance(type);
toBeCalled.Invoke(null, () => obj);

But it gives compile error:

Error CS1660: Cannot convert `lambda expression' to non-delegate type `object' (CS1660)

Which is absolutely acceptable but what's the work around?

Please keep in mind that the closure created using the lambda is something that I need, so don't eliminate it.

[UPDATE]

To clarify the situation so it makes sense; the functor created using the lambda is an instantiator used by SomeMethod to create an instance of the class SomeType. But I don't want to actually create the object in functor, instead return it from a collection of instantiated objects from before. The easiest way would be having the obj in a closure returned by the lambda, don't you think?

8
  • 1
    Well what's the type of obj? If you don't know the T involved, it's hard to see how you can create a Func<T> from a lambda expression. Are you always just returning a constant value, or would you sometimes have other lambda expressions? More information would be really helpful. Commented Jul 17, 2015 at 15:48
  • @JonSkeet I've updated the question Commented Jul 17, 2015 at 15:58
  • But you want to create a Func which always just returns obj? Nothing more complicated than that, ever? You say you want to keep the closure - does that mean you're going to change the value of obj afterwards? Again, more clarity would be really helpful. Commented Jul 17, 2015 at 16:00
  • Can we change SomeMethod()? Commented Jul 17, 2015 at 16:00
  • @JamesCurran No, actually SomeMethod belongs to a library and I didn't write it! Commented Jul 17, 2015 at 16:09

3 Answers 3

3

I think you a looking to pass known object ot generic method that takes Func<T> and that "func" is always identity. One more wrapper may help:

public void SomeMethodInvokerRuntime(Type typeofSomeClass, object obj)
{
    var _SomeMethod = this.GetType().GetMethod("SomeMethodInvoker", 
        BindingFlags.Public | BindingFlags.Static);
    MethodInfo toBeCalled = _SomeMethod.MakeGenericMethod(obj.GetType());
    toBeCalled.Invoke(null, new[]{typeofSomeClass, obj});
}

public static void SomeMethodInvoker<T>(Type typeofSomeClass, T obj)
{
    var _SomeMethod = typeofSomeClass.GetMethod("SomeMethod",
          BindingFlags.Public | BindingFlags.Static);
    MethodInfo toBeCalled = _SomeMethod.MakeGenericMethod(typeof(T));
    Func<T> that = () => obj; // Main part - strongly typed delegate
    toBeCalled.Invoke(null, new[]{that});
}

Sample usage:

static class Test
{
    public static void SomeMethod<T>(Func<T> f)
    {
        Console.WriteLine((T)(f()));
    }
}

SomeMethodInvoker(typeof(Test), 3);
object obj = "test";

SomeMethodInvokerRuntime(typeof(Test), obj);

Alternatively you can build expression and compile into a function as shown in Jon Skeet's answer.

Sign up to request clarification or add additional context in comments.

8 Comments

PS I may post sample for expression approacy if this is what OP is looking for.
The problem is you have Type typeofSomeClass, but not T obj. T needs to be set at compile time, but we don't know it until runtime.
@JamesCurran but you already know how to call generic function with unknown type (MakeGenericMethod on SomeMethodInvoker and pass methodInfoOfSomeMethodInvoker.Invoke(null, obj)), so I assume that the only problem is how to build Func<some-run-time-type>... Would that provide complete answer?
Ignore this answer - Jon Skeet awaited long enough :) and posted awesome answer.
@AlexeiLevenkov I like yours better. It's much simpler and easier to understand. Thanks.
|
3

It doesn't sound like you really need a lambda expression here. You can build a function to return a constant value very easily using expression trees:

var expression = Expression.Constant(obj, type);
var delegateType = typeof(Func<>).MakeGenericType(type);
var func = Expression.Lambda(delegateType, expression).Compile();
toBeCalled.Invoke(null, func);

Comments

0

Reduced to it's simplest form, we have this:

void Main()
{
    MethodInfoInvoke(()=> {} );
}

void MethodInfoInvoke(object func)
{
}

Which fails with the same error.

Now, if we pass the lambda in a delegate, it seems happier:

void Main()
{
    Object obj = new Object();
    Func<object> action = ()=> obj; ;
    MethodInfoInvoke(action);      // accepted!
}

1 Comment

Your reduction is not proper. You can not reduce having SomeMethod<T>(Func<T>), introduce such a method and try calling it with a lambda as the parameter using reflection and then you'll see the problem. Your current solution does not work then.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.