Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to activate a generic method that takes an action as its parameter

Tags:

c#

.net

generics

How would you go about using reflection to execute the following method when the type can only be inferred at runtime?

MainObject.TheMethod<T>(Action<OtherObject<T>>)

in everyday use, typically:

mainObject.Method<Message>(m => m.Do("Something"))

So, given a list of types, I need to substitute them for T in the method above and invoke.

This is where I got before my head to turned to putty:

var mapped = typeof(Action<OtherObject<>>).MakeGenericType(t.GetType());
Activator.CreateInstance(mapped,  new object[] { erm do something?});

typeof(OtherObject)
    .GetMethod("TheMethod")
    .MakeGenericMethod(t.GetType())
    .Invoke(model, new object[] { new mapped(m => m.Do("Something")) });

Update: For clarification, i have a list of types and i wish to execute the same known method of OtherObject for each. Pseudo-code:

foreach(var t in types)
{
    mainObject.TheMethod<t>(mo => mo.Do("Something"))
}

(The type of the parameter for TheMethod() is Action<OtherObject<T>> as stated above)

FluentNHibernate.Automapping.AutoPersistenceModel Override<T>(System.Action<AutoMapping<T>> populateMap)

the action is the same of AutoMapping<T>.Where("something")

model.Override<Message>(m => m.Where("DeletedById is null"))

Now, do that for a bunch of types :)

like image 993
jenson-button-event Avatar asked Sep 14 '11 14:09

jenson-button-event


People also ask

Can you directly invoke generic method using .NET reflection?

Calling a generic method with a type parameter known only at runtime can be greatly simplified by using a dynamic type instead of the reflection API. To use this technique the type must be known from the actual object (not just an instance of the Type class).

Where exactly type parameter need to be specified when declaring generic method?

While defining a generic method you need to specify the type parameter within the angle brackets (< T >). This should be placed before the method's return type.


1 Answers

You can solve this by using expressions:

foreach(var t in types)
{
    var mapped = typeof(AutoMapping<>).MakeGenericType(t);

    var p = Expression.Parameter(mapped, "m");
    var expression = Expression.Lambda(Expression.GetActionType(mapped),
                                       Expression.Call(p, mapped.GetMethod("Do"),
                                       Expression.Constant("Something")), p);

    typeof(SomeOtherObject).GetMethod("TheMethod").MakeGenericMethod(t)
                           .Invoke(model, new object[] { expression.Compile() });
}

UPDATE: Complete working example (paste into LINQPad and run it):

void Main()
{
    var types = new []{typeof(string), typeof(Guid)};
    SomeOtherObject model = new SomeOtherObject();
    foreach(var t in types)
    {
        var mapped = typeof(AutoMapping<>).MakeGenericType(t);

        var p = Expression.Parameter(mapped, "m");
        var expression = Expression.Lambda(
                             Expression.GetActionType(mapped),
                             Expression.Call(p, mapped.GetMethod("Do"),
                             Expression.Constant("Something")), p);

        typeof(SomeOtherObject).GetMethod("TheMethod")
                               .MakeGenericMethod(t)
                               .Invoke(model,
                                       new object[] { expression.Compile() });
    }
}

class AutoMapping<T>
{
    public void Do(string p)
    {
        Console.WriteLine(typeof(T).ToString());
        Console.WriteLine(p);
    }
}

class SomeOtherObject
{
    public void TheMethod<T>(Action<AutoMapping<T>> action)
    {
        var x = new AutoMapping<T>();
        action(x);
    }
}
like image 191
Daniel Hilgarth Avatar answered Sep 28 '22 07:09

Daniel Hilgarth