Reputation: 2339
How can I create a closure that uses a reflected type argument? Targeting .net 3.5
Without reflection I would have
void Main()
{
int i = 0;
Action<Foo> doSomething = (foo) => i += foo.GetNumber();
var myFoo = new Foo();
myFoo.UseFoo(doSomething);
Console.WriteLine(i);
}
class Foo
{
public int GetNumber() { return 4; }
public void UseFoo(Action<Foo> doSomething)
{
doSomething(this);
}
}
When Foo
is a type obtained via reflection from another assembly, how would I set doSomething
?
void Main()
{
Type fooType = GetType("Foo");
int i = 0;
object doSomething = // ???;
var myFoo = Activator.CreateInstance(fooType);
fooType.GetMethod("UseFoo").Invoke(myFoo, new object[] { doSomething });
Console.WriteLine(i);
}
Upvotes: 5
Views: 435
Reputation: 2339
I used both DocXcz and Nikola Anusev's answers to get to this
static void Main()
{
var fooType = typeof(Foo); // get type via any method
int i = 0;
Action<object> doSomething = (foo) => i += (int)foo.GetType().GetMethod("GetNumber").Invoke(foo, null);
var typedDoSomething = (typeof(Program)).GetMethod("DelegateHelper").MakeGenericMethod(fooType).Invoke(null, new object[] { doSomething });
var myFoo = Activator.CreateInstance(fooType);
fooType.GetMethod("UseFoo").Invoke(myFoo, new object[] { typedDoSomething });
Console.WriteLine(i);
}
public static Action<T> DelegateHelper<T>(Action<object> generic)
{
return x => generic(x);
}
Basically I needed to use a generic helper method to do the conversion of the Action<object>
to an Action<T>
determined at runtime
Upvotes: 3
Reputation: 7088
I am convinced that it is impossible to do exactly what you want on .NET 3.5.
Only way that comes close does not make use of lambdas, but plain old methods. Essentially, you would have your actions defined in a class similar to the following:
public class FooActionMethods<TFoo>
{
public static void DoSomething(TFoo foo)
{
int i = 0;
int number = (int)typeof(TFoo).GetMethod("GetNumber").Invoke(foo, null);
Console.WriteLine(i + number);
}
}
Then, you could invoke, for example, DoSomething
method:
Type fooType = // somehow, we get the type from other assembly
object fooInstance = Activator.CreateInstance(fooType);
Type fooActionType = typeof(Action<>).MakeGenericType(fooType);
Type fooActionMethodsType = typeof(FooActionMethods<>).MakeGenericType(fooType);
Delegate action = Delegate.CreateDelegate(fooActionType, fooActionMethodsType, "DoSomething");
fooType.GetMethod("UseFoo").Invoke(fooInstance, new object[] { action });
As the whole FooActionMethod
class is generic, we can make use of reflection (MakeGenericType
) and create the closed FooActionMethod
with the exact type of Foo
. Since we have no information about TFoo
at compile time, it is only possible to interact with its instances via reflection. To simplify these interactions, check out the SO question that dealt with some libraries that would simplify working with reflection.
Other than that, I think there is nothing better you could do. Thanks for the good question - makes for an excellent puzzle! :)
Just as a sidenote, I was also trying to solve this by using expression trees to create lambda expression dynamically. I was not able to find a way to include closures with Expression trees. Otherwise, that method would work fine as well.
Upvotes: 1
Reputation: 289
If you don't have statically linked that assembly containing Foo class at compile time, you are true that you cannot use Foo in the code.
You must use reflection also in the closure:
Action<object> doSomething =
(foo) => i+= (int)foo.GetType().GetMethod("GetNumber").Invoke(foo, null);
This assumes that Foo's methos UseFoo is something like this and only works with .NET 4.
public void UseFoo(Action<Foo> action)
{
action(this);
}
Of course, you should enclose whole calling of UseFoo to try..catch block, because anything in reflection is not guaranteed.
Update: This works only in .NET 4, where Action can be passed as Action assumed by the UseFoo method.
Upvotes: 1
Reputation: 171236
If foo implements a suitable interface you can have the Action
work on this interface.
If not, you need to either use reflection in your delegate (easier) or compile a delegate at runtime using the expression API (more work, max. performance).
As the C# compiler creates the closure to i
you need to keep doing that:
Action<int> incrementI = inc => i += inc;
You can now use increment i
in your compiled delegate to increment i
.
Upvotes: 0
Reputation: 35869
A closure, in C#, is something generated by the compiler. If you wanted the same code generated for you reflected type, you'd have to generate the code manually.
Upvotes: 0