Search code examples
c#exceptiondelegatesdelayed-execution

Add arguments one by one to a delegate then execute it


Yesterday I wanted to create an ExceptionHandler class to which for example we could pass:

  • the exception
  • the method where that happened
  • the parameters given to that method

The ExceptionHandler would implement a custom business logic and re-execute automatically (or not) the method with the same parameters.

The closest solution I found was by using a delegate like this

private void button1_Click(object sender, EventArgs e)
{
    InvokeMyMethod(Test, "string", 1);
    InvokeMyMethod(TestN, "other string", 3, Color.Red);
}

public delegate void MyMethodDelegate(params object[] args);

public void InvokeMyMethod(MyMethodDelegate method, params object[] args)
{
    method.DynamicInvoke(args);
}

public void Test(params object[] args)
{
    if (args.Length < 2) return;
    MessageBox.Show(string.Format("{0}{1}", args[0], args[1]));
}

public void TestN(params object[] args)
{
    // or, whatewer
    if (args.Length < 3) return;
    MessageBox.Show(string.Format("{0}{1}{2}", args[0], args[1], args[2]));
}

However this solution is still not perfect because of the methods' signature. I don't want all my methods receive a unique parameter params object[] args, moreover this is often not possible directly for example with all UI related events (Winform, ASP.NET, WPF or whatever).

So I'd like to know if there is a way to pass one by one arguments to a delegate and then execute the function only when we are ready?

For example here is an imaginary code with non-existing methods AddArgument and Execute

public void InvokeMyMethod(MyMethodDelegate method, params object[] args)
{
    foreach(var arg in args)
    {
        method.AddArgument(arg);
    }
    method.Execute();
}

By this way all our functions could keep their original signature and receive multiple arguments instead of an array.


Solution

  • Your original code isn't far off at all. You can get rid of requiring all the called methods have the same signature as long as you make InvokeMyMethod just take the abstract Delegate class and you explicitly specify the delegate type when you call the function (it won't infer the type). We can take advantage of the fact that there are generic delegate types in C# so we don't need to declare every possible set of method signatures that we want to use.

    If you wanted to call functions with a return value you'd need to use Func<> in place of Action<>.

    private void button1_Click(object sender, EventArgs e)
    {
        InvokeMyMethod((Action<string,int>) Test, "string", 1);
        InvokeMyMethod((Action<string,int,Color>) TestN, "other string", 3, Color.Red);
    }
    
    public void InvokeMyMethod(Delegate method, params object[] args)
    {
        method.DynamicInvoke(args);
    }
    
    public void Test(string s, int i)
    {
        MessageBox.Show(string.Format("{0}{1}", s, i));
    }
    
    public void TestN(string s, int i, Color c)
    {
        // or, whatewer
        MessageBox.Show(string.Format("{0}{1}{2}", s, i , c);
    }