2

I am trying to create an extension method that takes a func with arguments. I want to support variable number of arguments (none, 1, 2, ...10)

I have something like this that works for exactly three arguments. How can I simplify it to support variable number of arguments without having to copy-and-paste for every permutation? Is it even possible?

(NOTE: my example is very bare-bones. My real implementation has a lot more logic, e.g. supporting 'Retry' logic that has counts, Thread.Sleep, trace logging, exception handling, etc.)

Thanks!

public static class UtilExtensions
{
    public static TResult Execute<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> function, T1 argument1, T2 argument2, T3 argument3))
    {
        // do other stuff ... like logging

        try
        {
           // call our 'action'
           TResult result = function(argument1, argument2, argument3);
           return result;
        }
        catch (Exception ex)
        {
           // do other stuff ... like logging, handle Retry logic, etc.
        }
    }
}

and it is invoked like this:

public string DoSomething(int arg1, string arg2, MyObject arg3)
{
    if (arg1 == 1)
        throw new Exception("I threw an exception");

    return "I ran successfully";
}
public string DoSomethingElse()
{
    return "blah blah blah";
}

public string DoSomethingMore(DateTime dt)
{
    return "hi mom";
}


[TestMethod]
public void Should_call_UtilsExtensions_Execute_method_successfully()
{
    int p1 = 0;
    string p2 = "Hello";
    MyObject p3 = new MyObject();

    string results = UtilExtensions.Execute<int, string, MyObject, string>(
        DoSomething, p1, p2, p3);

    // ??? So how would I use my UtilExtensions api to call
    // DoSomethingElse (no arguments)
    // DoSomethingMore (one argument)
    // I'm okay to create overloads of my Execute method
    // but I don't want to copy-and-paste the same code/logic in each method

    results.Should().Be("I ran successfully");
}
7
  • msdn.microsoft.com/en-us/library/w5zay9db.aspx Commented Feb 2, 2016 at 21:03
  • If I understand correctly what you want is something similar to C++ variadic templates. Look at this answer: stackoverflow.com/questions/6844890/… Commented Feb 2, 2016 at 21:04
  • @Lee Taylor. I don't think params will work... but would be happy if you can show me otherwise. Commented Feb 2, 2016 at 21:12
  • 2
    I believe params will work if you give up your strong typednesss and make an extension method on System.Delegate instead Commented Feb 2, 2016 at 21:15
  • @Raymond - please explain why you think params wouldn't work Commented Feb 2, 2016 at 21:15

2 Answers 2

2

I don't think you can do this and as far as I know this is exactly the reason the Func and Action types have numerous distinct declarations for different parameter numbers in .Net. See the left hand side of https://msdn.microsoft.com/en-us/library/018hxwa8(v=vs.110).aspx

You are asking something similar to the following except in this case the OP was asking about interfaces Can I have a variable number of generic parameters?

The problem is essentially the same fundamental limitation of generics.

You might be able to do something with reflection but I think you'd be better picking a sensible number of parameters to support and write overloads.

Params won't work because for params all types in the array are the same type. T1, T2 etc are different types for your use. You'd have to declare the params as Object[] which completely defeats the point of generics. Although perhaps you are willing to give up the type checking if you truly have an unknown number of parameters likely to be impractically high.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, that's what I thought... perhaps it is time for a code generator.
@Raymond yeah I have seen a code generator used for this exact purpose before. It feels dirty but might actually be the best solution. As long as you've considered the implications of the other possible solutions and decided this is best for this one case then I think it's OK.
1

You can't do that. An alternative is to create an extension method that accepts one argument, but that argument can be made up of anonymous functions. So your calling code will have full control over how to use it. Not the cleanest, but at least a little cleaner than a code generator route:

public static class UtilExtensions
{
    public static TResult Execute<TResult>(this Func<TResult> function)
    {
        // do logging

        try
        {
            TResult result = function();
            return result;
        }
        catch (Exception ex)
        {
            // do other stuff ... like logging, handle Retry logic, etc.
        }

        // or throw - this right here could prove unpredictable and is verrrry dirty
        return default(TResult);
    }
}

Usage:

var param1 = 1;
var param2 = "string";

Func<bool> function = () => 
{
    // do stuff with param1 and param2
    return true;
}

var results = function.Execute();

2 Comments

I do really like this approach and is what I ended up using... however, I still had to copy-and-paste my code 4 times. one each for Action vs Func<TResult>, and again for async method support. I may still consider a code-generated approach.
@Raymond yeah I imagined you'd have to repeat the code essentially 4 times - can't think of a way around that. Although... that code would sit in the same place and would have little reason change afterwards. I'd be curious to know if a code-generated approach ends up being worth the time effort.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.