簡體   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