Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I've written a piece of an ugly code. The idea was to provide a clear-looking, type-inferring framework to test a task-based API (it's for integration testing, and test methods should be as clear as possible).

All the tested API functions look like this (number of parameters are 0 to 4):

Task<TResult> SomeFunction((here go parameters ...),
                           CancellationToken token,
                           TaskScheduler scheduler,
                           TaskCreationOptions options);

The idea was (and it works!) that I could call those tasks as following:

// Start, wait for the specified time, and throw exception if it fails to complete in time
Do.Exec(SomeFunction, [param1, [param2, [...,]]] timeout);
// Start, wait for the specified time, and return false if it fails to complete in time
bool result = Do.Try(SomeFunction, [param1, [param2, [...,]]] timeout);
// Start, wait for the specified time, return result or throw exception if fails
var result = Do.Get(SomeFunction, [param1, [param2, [...,]]] timeout);
// Start, wait for the specified time, return result or default(TResult) if fails
var result = Do.Ask(SomeFunction, [param1, [param2, [...,]]] timeout);

So, I enjoy the result. The static Do class works with my tasks and reports necessary progress to log. I do not have to deal anymore with test freezes, I do not have to specify waiting behavior in test scripts. But I don't at all enjoy the implementation of Do class.

First, I determine a set of delegates to support variable parameter number:

public delegate Task<ResultType> ResultTaskDelegate<ResultType>
    (CancellationToken token, TaskScheduler sched, TaskCreationOptions options);
public delegate Task<ResultType> ResultTaskDelegate1<ResultType, ParamType1>
    (ParamType1 param1, CancellationToken token, TaskScheduler sched, TaskCreationOptions options);
public delegate Task<ResultType> ResultTaskDelegate2<ResultType, ParamType1, ParamType2>
    (ParamType1 param1, ParamType2 param2, CancellationToken token, TaskScheduler sched, TaskCreationOptions options);
public delegate Task<ResultType> ResultTaskDelegate3<ResultType, ParamType1, ParamType2, ParamType3>
    (ParamType1 param1, ParamType2 param2, ParamType3 param3, CancellationToken token, TaskScheduler sched, TaskCreationOptions options);

(And it's only a half, there are also delegates for tasks with no result)

Then, for example for Do.Get() set of methods:

public static ResultType Get<ResultType>(
    ResultTaskDelegate<ResultType> func,
    int timeout)
{
    return waitResultTask(func, func.Method.Name, timeout, true);
}

public static ResultType Get<ResultType, ParamType1>(
    ResultTaskDelegate1<ResultType, ParamType1> func,
    ParamType1 param1,
    int timeout)
{
    return waitResultTask((t, s, o) => func.Invoke(param1, t, s, o), func.Method.Name, timeout, true);
}

public static ResultType Get<ResultType, ParamType1, ParamType2>(
    ResultTaskDelegate2<ResultType, ParamType1, ParamType2> func,
    ParamType1 param1,
    ParamType2 param2,
    int timeout)
{
    return waitResultTask((t, s, o) => func.Invoke(param1, param2, t, s, o), func.Method.Name, timeout, true);
}

public static ResultType Get<ResultType, ParamType1, ParamType2, ParamType3>(
    ResultTaskDelegate3<ResultType, ParamType1, ParamType2, ParamType3> func,
    ParamType1 param1,
    ParamType2 param2,
    ParamType3 param3,
    int timeout)
{
    return waitResultTask((t, s, o) => func.Invoke(param1, param2, param3, t, s, o), func.Method.Name, timeout, true);
}

And the same horrible thing for Do.Exec, Do.Try and Do.Ask. (I do not post implementation of helping methods like waitResultTask(), it's of no interest).

The Question is: Can it be done in a more generic and prettier way?

share|improve this question

migrated from stackoverflow.com Nov 23 '12 at 19:24

This question came from our site for professional and enthusiast programmers.

4  
Have you looked at the Action<> and Func<> classes? They're not prettier than this. –  Davio Nov 14 '12 at 10:51
    
You should also look into using ninject.org for it allows an easy way of doing something that is reflection-like. –  Leonid Nov 24 '12 at 4:33

2 Answers 2

up vote 2 down vote accepted

You can use an anonymous methods:

public static class Do {
    public static TResult Get<TResult>(Func<TResult> func) {
        // your code here
        var result = func();
        // your code here
        return result;
    }
}
// ...
var result = Do.Get( ()=> myObject.SomeFunc("Hello world!", DateTime.Now) );

This way allows you define the Get/Exec/etc... methods only once (maybe twice, once for Func<T> and once for Action) and you can use any method signature you want (including out/ref parameteres) without creation any more delegates or static methods.

share|improve this answer
    
I think this pattern is called Template-Method. –  TcKs Nov 26 '12 at 1:25
    
That is surely shorter, thanks. Only, it will require to create numerous explicit lambas in calling code. So I use this approach for functions that do not match common template. –  Oleg Leontiev Nov 28 '12 at 9:19
    
You need only methods for Func<T> and Action. You will pass anonymous method which can be passed by delegate or by lambda expression (and compiler will compile it into anonymous method). –  TcKs Nov 28 '12 at 10:20
    
Unfortunately, this way every function call would look like Do.Get((t, s, o) => _proxy.MyFunc("Hello!", 0, t, s, o), 500) while now I can use Do.Get(_proxy.MyFunc, "Hello!", 0, 5000) which looks a bit cleaner. Besides, func.Method.Name gives me directly the name "MyFunc" which is used in logging, and for anonimous functions I need to dive deeper into reflection. I mean, your solution is really good and simple, but I thought about it and chose another way. –  Oleg Leontiev Nov 29 '12 at 9:22
    
No, you can call it: 'Do.Get(()=> _proxy.MyFunc("Hello!", 0, 500, someOtherVariable));'. The C# compiler will make proper way, to handle this calling. It's calling "closure". –  TcKs Nov 29 '12 at 11:44

Ok, thanks to everybody, but it seems that there are some language limitations in C#. What I would really love is Variadic Templates that have been introduced in C++11.

With C#, there are following alternatives:

  • Most obvious - use params modifier, analyze the number of params at runtime and use Invoke() with arguments. This is not suitable because it's dynamic and can not be used for static error detection (bad parameter types).
  • Using anonymous Action and Func<> (adviced by TcKs). I don't like the idea for reasons that are above in comments.
  • Using tuples or dictionaries for properties lists - this is not good at all, the function calls will contain too many constructive elements.
  • IoC libraries (adviced by Leonid) are rather good for resolving objects at class-level, but do not provide any generic parameter substitution (of course, we can specify default parameters for components, but it's a dubious way for loosely-structured test scenarios when we need to call functions with various parameters).
  • LambdaExpression (???) For this case, I could not see any suitable application for expressions that gave better usage than anonymous delegates.

Some of the above methods could be fine for complete programming architecture, but when it comes to testing scenarios, we prefer just a clear way to invoke commands.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.