0

I create a Delegate from an method I know only takes one parameter and later call it using a DynamicInvoke, but I was wondering if it was possible to get an Action to invoke directly.

Here is what I have currently:

private IEnumerable<MethodInfo> GetMethods()
            => GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

private IEnumerable<Thing> GetThings() {
    foreach (var method in GetMethods()) {
        var attribute = (MyAttribute) method.GetCustomAttribute(typeof(MyAttribute), false);
        var theDelegate = CreateDelegate(method);

        return new Thing(attribute.Name, theDelegate);
    }
}

private Delegate CreateDelegate(MethodInfo method) {
    Type requestType = method.GetParameters()[0].ParameterType,
         actionType = typeof(Action<>).MakeGenericType(requestType);

    return Delegate.CreateDelegate(actionType, this, method);
}

public void Invoke(Thing thing, string json) {
    var requestType = MyDelegate.Method.GetParameters()[0].ParameterType;
    var o = Deserialize(json, requestType);

    thing.TheDelegate.DynamicInvoke(o);
}

Using an Action, not only would it be faster but it would look much neater. The following code doesn't work but there must be a way to get something similar:

private Action CreateAction(MethodInfo method) {
    Type requestType = method.GetParameters()[0].ParameterType,
         actionType = typeof(Action<>).MakeGenericType(requestType);

    return (Action) Delegate.CreateDelegate(actionType, this, method);
}

public void Invoke(Thing thing, string json) {
    Type requestType = MyAction.GetParameters()[0].ParameterType;
    var o = Deserialize(json, requestType);

    thing.MyAction(o);
}
Hugo
  • 349
  • 3
  • 6
  • 23
  • How is this different from the original question that you deleted? https://stackoverflow.com/questions/55306799/how-to-invoke-methods-from-reflection-with-a-deserialized-json-object?noredirect=1#comment97409320_55306799 – Enigmativity Mar 25 '19 at 23:35
  • @Enigmativity I just want to know if it's possible, not because of performance issues but out of curiosity. It feels like there must be a way, I just can't figure it out. – Hugo Mar 26 '19 at 19:21
  • You can use reflection to call in to a strongly-typed method (which gives you want you want I think), but there is no way in a statically-typed language to get a strong-typed reference from a run-time reference. – Enigmativity Mar 26 '19 at 21:09

3 Answers3

0

Since Action<T> does not derive from Action, you cannot assign an Action<T> to Action. But both derive from Delegate, so you can assign them to Delegate.

I fear that you will have to stick to Delegate as return type.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

If you look at the reference source you'll see the following lines:

public delegate void Action<in T>(T obj); 
public delegate void Action(); 

Action and Action<T> are delegates, so I don't see a reason why this should be any faster.

But more important: you're code can't work because Action simply is no Action<T>, it does not take a parameter. They both derive from Delegate, but not from each other.

René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • I know my code can't work it was an example of the type of solution I'm looking for. For the reasons you can see [this thread](https://stackoverflow.com/questions/12858340/difference-between-invoke-and-dynamicinvoke) – Hugo Mar 25 '19 at 17:26
0

Since we don't know exactly where your CreateDelegate method is getting MethodInfo from I am going to assume you have direct access to the proper method. So with the following class we can cache a delegate that is invoked with a string of json.

public sealed class JsonInvoker<T>
{
    private readonly Action<T> _action;

    public JsonInvoker(Action<T> action)
    {
        _action = action;
    }

    public void Invoke(string json)
    {
        var arg = Deserialize(json);
        _action(arg);
    }

    private T Deserialize(string json) => //...
}

Then setup your supporting class.

private readonly JsonInvoker<DataClass> _dataClassInvoker = 
    new JsonInvoker<DataClass>(DataClass.Process); // Pass the relevant method
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
  • That means I need a `JsonInvoker` for every request type? How would I instanciate `_action`? I cannot cast my `Delegate` to `Action` – Hugo Mar 25 '19 at 18:37
  • @Halhex - Where are you getting the MethodInfo from? – ChaosPandion Mar 25 '19 at 19:03
  • `GetType().GetMethods(...)` checking if they have a certain attribute. – Hugo Mar 25 '19 at 19:31
  • @Halhex - Can you post the code that collects those up? I do have a basic idea of how you might pull off something useful. – ChaosPandion Mar 25 '19 at 19:38
  • I added the code, though I don't see how it would be useful. – Hugo Mar 25 '19 at 19:56
  • @Halhex - So it looks like you do have access to all the methods in one class but unfortunately it seems like the only way to avoid invoke dynamic is to build up the sequence of Things by hand passing in the delegate directly as seen in my example. Think about it and do what makes sense to you. – ChaosPandion Mar 25 '19 at 20:02