Search code examples
c#genericslambdaexpression-trees

Pass generic method omitting its parameters?


Is there a way to pass a method name in a generic manner, without passing its parameters, so it can be invoked by the method, with passed arguments?

Consider this example:

public class Client
{
  public string Convert(int value)
  {
    return value.ToString();
  }
}

public class Wrapper<TClient>
{
  TClient Client;

  public TResult Invoke<TArg, TResult>(Func<TClient, TArg, TResult> action, TArg arg)
  {
    return action(Client, arg);
  }
}

I want to be able to pass to the wrapper the method of TClient I want to invoke, and pass the actual arguments along, all generically:

var wrapper = new Wrapper<Client>();
wrapper.Invoke(c => c.Convert, 5);

Is there any possible way to achieve that, without hard coding the method name, or losing its genericness (i.e. by using Delegate)?

Notes:

The Client is an external sealed class that exposes a gazillion methods each of many parameters. I want wrap its behavior and I don't mind writing all the necessary code in the wrapper, but the usage of the wrapper should be as clean as possible.

Update
I want to avoid the need to specify the parameters. The whole idea is having them inferred from the specified action.


Solution

  • You're very close to getting your code to run. There are two options.

    First, you can try this:

    public class Wrapper<TClient>
    {
        public TResult Invoke<TArg, TResult>(Func<TArg, TResult> action, TArg arg)
        {
            return action(arg);
        }
    }
    

    Then call it like this:

    var wrapper = new Wrapper<Client>();    
    wrapper.Invoke(wrapper.client.Convert, 5);
    

    Or, alternatively, you can do this:

    public class Wrapper<TClient>
    {
        public Wrapper(TClient client)
        {
            this.Client = client;
        }
    
        private TClient Client;
    
        public TResult Invoke<TArg, TResult>(Func<TClient, TArg, TResult> action, TArg arg)
        {
            if (operation.Target != Client)
              throw new ArgumentException(nameof(operation));
    
            return action(this.Client, arg);
        }
    }
    

    And call it like this:

    var client = new Client();
    var wrapper = new Wrapper<Client>(client);
    
    wrapper.Invoke((c, a) => c.Convert(a), 5);
    

    But, from your description of your problem, I don't see how either of these help and I don't see how to implement what you're asking. Perhaps you need to provide more detail as to what the underlying need you're trying to solve?