Josh
Josh

Reputation: 2339

How can I create a delegate closure with a reflected type

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

Answers (5)

Josh
Josh

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

Nikola Anusev
Nikola Anusev

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

Luk&#225;š Doležal
Luk&#225;š Doležal

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

usr
usr

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

Peter Ritchie
Peter Ritchie

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

Related Questions