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?