1

I have a C# code

private class EvaluationTask : Task<Solution> {
    private Problem problem_;
    private Solution solution_;
    public EvaluationTask(Problem problem, Solution solution)
    {
        problem_ = problem;
        solution_ = solution;
    }
}

Now, I am getting error System.Threading.Tasks.Task<> does not contain constructor that takes 0 arguments. From previous answers posted, I found that one has to define empty constructor in the base class. But since my base class is Task<>, how do I add an empty constructor to it?

Any help would be highly appreciated!

Edit: I have to inherit task<> because I have to use the method EvaluationTask in a code:

taskList_ = new List<Task<Solution>>();
taskList_.Add(new MultithreadedEvaluator.EvaluationTask (problem_, solution));

I don't know about task composition, so if it is necessary can anyone help with that? Or if by any way I can avoid inheriting Task and still implement taskList_.Add()?

Saniya
  • 59
  • 3
  • There should be no reason at all for you to inherit `System.Threading.Tasks.Task<>`. Have you tried composition, have you taken a look at the `IAwaitable<>` interface? Does your implementation adhere to the Liskov Substitution Principle? – Aron Jun 23 '16 at 05:04

3 Answers3

2

When you inherit from a class, in your constructors you need to call any of the constructors of the base class. In your case, since you aren't calling any constructor, the compiler try to call a parameterless constructor of the base class, but Task<> haven't a parameterless constructor.

As you can read here, inheriting from Task<> probably isn't a good idea, but you can do something like this:

class EvaluationTask : Task<Evaluation>
{
    public EvaluationTask() 
        : base(DoWork) { }

    private static Evaluation DoWork()
    {
        //...
    }
}
Community
  • 1
  • 1
Arturo Menchaca
  • 15,783
  • 1
  • 29
  • 53
  • This is a perfectly good and correct answer, but unfortunately the `DoWork` function has to be `static` and thus has no obvious way of accessing any instance members that likely need to exist as the very point of overriding `Task` in the first place. In another [answer](https://stackoverflow.com/a/56489462/147511) on this page, I provide a workaround for this problem, in case you're interested. – Glenn Slayden Jun 07 '19 at 07:06
1

When using Task<T>, you must supply the Func<> or Action<> delegate (i.e., function pointer to the desired work to perform) as a constructor argument. It is indeed somewhat unfortunate that there isn't any constructor which lets you bypass this requirement and supply the delegate at a later time (yet obviously still prior to calling Start()), since this severely hampers the ability to extend the Task and Task<TResult> classes via inheritance altogether.

The reason it's a crippling omission is that no delegate you supply as a constructor argument can possibly directly incorporate a reference to the instance you are trying to construct, since that instance (again, obviously) doesn't exist yet, chicken/egg style.

Hence @Arturo's answer, which shows that you can, in fact, supply a static delegate, but since such a delegate has no obvious way of referencing one particular Task instance, it essentially defeats the purpose of inheriting from Task in the first place.

--- reflection disclaimer ---
I've been using this technique in my own projects for years on .NET Framework 4.7 (desktop) with no problems whatsoever, but please note that code which uses reflection to access non-public behavior is subject to breakage if the .NET internals change in a later version. You have been warned.


Here's a more flexible workaround for the problem, a general-purpose abstract base class for Task<TResult> which allows you to provide the desired work code in the normal way for derived type hierarchies: as an instance method override. This is a reflection solution; the way it works is to provide a dummy "placeholder" delegate to the base constructor call, but then immediately in the constructor body, swap it out for the "real," desired abstract instance work method, which at that point is no longer unknowable or rather unbindable.

abstract class TaskBase<TResult> : Task<TResult>
{
    readonly static FieldInfo m_action =
       typeof(Task).GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic);

    readonly static Func<TResult> _dummy = () => default;

    public TaskBase(CancellationToken ct, TaskCreationOptions opts)
        : base(_dummy, ct, opts) =>
            m_action.SetValue(this, (Func<TResult>)function);

    public TaskBase(CancellationToken ct)
        : this(ct, TaskCreationOptions.None)
    { }

    public TaskBase(TaskCreationOptions opts)
        : this(default, opts)
    { }

    public TaskBase()
        : this(default, TaskCreationOptions.None)
    { }

    protected abstract TResult function();  // <-- override with your work code
};

To use this, simply inherit from TaskBase<TResult>, and override the abstract method function() to implement your task work logic. There is no need for a version of this base class where the work function accepts a AsyncState argument/parameter(s), since you can simply declare all the relevant context for the specific work instance as additional instance fields (and instance methods, and instance properties...) in your derived class. So the constructor variations I declared exactly match those provided by Task<TResult>, but minus the 'function' and 'state' arguments. And finally, don't forget to call Start() when your packaged work instance is ready to go!


The above is a actually a simplified version of the TaskBase<TResult> code I've had much success with. My enhanced version avoids creating the Func<TResult> delegate which must be created for each TaskBase instance in order to "wrap" the C# method function() as an instance delegate. Instead of initially providing a 'dummy' delegate to the base constructor, I always provide (the same) static delegate, a singleton which acts as a "thunk" that universally reinterprets, or "upcasts" a Task<TResult>'s AsyncState object as a pertinent TaskBase<TResult> instance, and then calls function() directly on that instance. Like so:

static Func<Object,TResult> thunk = obj => ((TaskBase<TResult>)obj).function();

So fn_redirect is the only "excess" delegate we need to create once at startup, and this singleton is always passed-in as the base constructor work delegate. Now as with that constructor argument, the "async state" object is also only passed in as a constructor argument and normally cannot later be changed. We don't need a "dummy" in this approach, because you can--and should--pass in 'null' for state. Similar to before we use reflection to set a field, but this time it's m_stateObject field instead of m_action, to replace the 'null' value we just installed for the instance this pointer:

public TaskBase(CancellationToken ct, TaskCreationOptions opts)
    : base(thunk, default(Object), ct, opts)
{
    m_stateObject.SetValue(this, this);
}

Voila, allocating one extra delegate for each TaskBase instance is avoided. Finally, recall that there are no adverse loss of capability when co-opting the state object for the purpose of this enhancement because as I mentioned earlier, the whole AsyncObject argument-passing mechanism is unnecessary when you entirely control the derived class you are writing.

Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
0

Here is an inheritable class that inherits from Task<T>, and allows delayed assignment of the task's function. The constructor takes no arguments. The function is assigned by the property Function.

public class FlexibleTask<T> : Task<T>
{
    private readonly Helper _helper;

    public Func<T> Function { set { _helper.SetFunction(value); } }

    public FlexibleTask() : base(GetFunction())
    {
        this._helper = TempHelper;
        TempHelper = null;
    }

    private static Func<T> GetFunction()
    {
        Func<T> function = Default;
        var helper = new Helper();
        helper.SetFunction = f => function = f;
        TempHelper = helper;
        return () => function();
    }

    private static readonly Func<T> Default = () =>
        throw new InvalidOperationException("Function is not set.");

    [ThreadStatic] private static Helper TempHelper;

    private class Helper
    {
        public Action<Func<T>> SetFunction {get; set;}
    }
}

Usage Example:

public class EvaluationTask : FlexibleTask<int>
{
}

var task = new EvaluationTask();
task.Function = () => 13;
task.Start();
var result = await task;
Console.WriteLine($"Result: {result}");

Output:

Result: 13

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104