繁体   English   中英

基类Task <>没有空构造函数

[英]Base class Task<> has no empty constructor

我有一个C#代码

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

现在,我收到错误System.Threading.Tasks.Task<>不包含带0参数的构造函数。 从之前发布的答案中,我发现必须在基类中定义空构造函数。 但由于我的基类是Task<> ,我该如何向它添加一个空构造函数?

任何帮助将非常感谢!

编辑:我必须继承任务<>因为我必须在代码中使用EvaluationTask方法:

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

我不知道任务组成,所以如果有必要,任何人都可以帮忙吗? 或者,如果以任何方式我可以避免继承任务并仍然实现taskList_.Add()?

从类继承时,在构造函数中需要调用基类的任何构造函数。 在您的情况下,由于您没有调用任何构造函数,编译器会尝试调用基类的无参数构造函数,但Task<>没有无参数构造函数。

你可以在这里阅读,继承Task<>可能不是一个好主意,但你可以这样做:

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

    private static Evaluation DoWork()
    {
        //...
    }
}

使用Task<T> ,必须提供Func<>Action<>委托(即,要执行的所需工作的函数指针) 作为构造函数参数 确实有点不幸的是,没有任何构造函数允许您绕过此要求并在稍后提供代理(但显然仍然在调用Start() ),因为这严重阻碍了扩展TaskTask<TResult>的能力Task<TResult>类通过继承完全。

这是一个令人遗憾的遗漏的原因是,你提供的构造函数参数的任何委托都不能直接包含对你想要构建的实例的引用,因为那个实例(显然,再次)还不存在,鸡/蛋样式。

因此,@ Arturo的回答表明,事实上,你可以提供静态委托,但由于这样的委托没有明显的方法来引用一个特定的Task实例,所以它基本上违背了从Task继承的目的。

--- 反思免责声明 ---
我已经在我自己的项目中使用这种技术多年来在.NET Framework 4.7 (桌面)上没有任何问题,但请注意,如果.NET内部更改,使用反射来访问非公共行为的代码会受到破坏在更高版本中。 你被警告了。


这是针对该问题的更灵活的解决方法, Task<TResult>的通用抽象基类,它允许您以常规方式为派生类型层次结构提供所需的工作代码:作为实例方法覆盖。 这是一种反思解决方案; 它的工作方式是为基础构造函数调用提供一个虚拟的“占位符”委托,但是然后立即在构造函数体中 ,将其交换为“真实的”所需的抽象实例工作方法,此时该方法不再是不可知的或者说是不可能的

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
};

要使用它,只需从TaskBase<TResult>继承,并覆盖抽象方法function()以实现任务工作逻辑。 不需要工作函数接受AsyncState参数/参数的此基类的版本,因为您可以简单地将特定工作实例的所有相关上下文声明为附加实例字段(以及实例方法和实例)属性...)在派生类中。 所以我声明的构造函数变量与Task<TResult>提供的变量完全匹配,但减去了'function'和'state'参数。 最后,当您的打包工作实例准备就绪时,不要忘记调用Start()


以上是TaskBase<TResult>代码的实际简化版本,我已经取得了很大的成功。 我的增强版避免了创建必须为每个TaskBase实例创建的Func<TResult>委托,以便将C#方法function() “包装”为实例委托。 我总是提供(相同的)静态委托,而不是提供(相同的)静态委托,作为“thunk”的单例,它通用地重新解释或“向上转发” Task<TResult>AsyncState对象,而不是最初向基础构造函数提供“虚拟”委托。作为一个相关的TaskBase<TResult>实例,然后直接在该实例上调用function() 像这样:

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

所以fn_redirect是我们在启动时需要创建的唯一“多余”委托,并且这个单例始终作为基础构造函数工作委托传入。 现在与该构造函数参数一样,“异步状态”对象也仅作为构造函数参数传递,通常以后不能更改。 在这种方法中我们不需要“虚拟”,因为你可以 - 而且应该 - 为state传递'null'。 类似于我们使用反射之前设置一个领域,但这次是m_stateObject场,而不是m_action ,以取代我们刚刚安装实例的“空”值this指针:

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

瞧,避免为每个TaskBase实例分配一个额外的委托。 最后,回想一下,为了这个增强而选择状态对象时没有不利的能力损失,因为正如我前面提到的,当你完全控制你正在编写的派生类时,整个AsyncObject参数传递机制是不必要的。

这是一个继承自Task<T>的可继承类,允许延迟分配任务的功能。 构造函数不带参数。 该功能由属性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;}
    }
}

用法示例:

public class EvaluationTask : FlexibleTask<int>
{
}

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

输出:

结果:13

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM