我正在尝试创建一个具有事件并且可以等待的类,但始终遇到绊脚石。

首先,我尝试了一个TransferJob类,该类返回一个TransferTask对象,该对象在返回时已经在运行。这将通过以下方式完成:

public abstract class TransferJob
{
    public TransferTask Start()
    {
        return Start(CancellationToken.None);
    }

    public TransferTask Start(CancellationToken token)
    {
        TransferTask task = CreateTransferTask();
        task.Start(token);

        return task;
    }

    protected abstract TransferTask CreateTransferTask();
}

public abstract class TransferTask
{
    public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;

    private Task transferTask;
    private TransferStatus status;

    public TransferStatus Status
        {
            get { return this.status; }
            protected set
            {
                TransferStatus oldStatus = this.status;
                this.status = value;

                OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
            }
        }

    internal void Start(CancellationToken token)
    {
        this.transferTask = TransferAsync(cancellationToken);
    }

    protected abstract Task TransferAsync(CancellationToken cancellationToken);

    protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
        {
            if (this.StatusChanged != null)
            {
                this.StatusChanged(this, txStatusArgs);
            }
        }

    public TaskAwaiter GetAwaiter()
    {
        return this.transferTask.GetAwaiter();
    }
}


上面的问题是,如果TransferTask非常快速地完成,那么TransferJob.Start()的用户可能没有时间在完成之前在返回的TransferTask的StatusChanged事件上注册其事件处理程序。因此,我尝试了另一种方法,即用户必须自己调用TransferTask的Start()方法。这将使用户有时间在TransferTask调用和transferJob.CreateTask()调用之间在transferTask.Start()上注册其事件处理程序:

public abstract class TransferJob
{
    public abstract TransferTask CreateTask();
}

public abstract class TransferTask
{
    public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;

    private Task transferTask;
    private TransferStatus status;

    public TransferStatus Status
        {
            get { return this.status; }
            protected set
            {
                TransferStatus oldStatus = this.status;
                this.status = value;

                OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
            }
        }

    public void Start(CancellationToken token)
    {
        this.transferTask = TransferAsync(cancellationToken);
    }

    protected abstract Task TransferAsync(CancellationToken cancellationToken);

    protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
        {
            if (this.StatusChanged != null)
            {
                this.StatusChanged(this, txStatusArgs);
            }
        }

    public TaskAwaiter GetAwaiter()
    {
        return this.transferTask.GetAwaiter();
    }
}


现在,我有另一个问题。如果用户在调用await transferTask;之前尝试使用transferTask.Start();,则可能会因为未启动任务(因此将其分配给NullReferenceException字段)而抛出transferTask。我真的在努力解决这个问题。有办法吗?还是使用比以上更好的模式?

最佳答案

我真的不相信这是个好主意。只需公开TAP模式。删除事件以及transferTaskStart的调用者必须保留该任务,并将其传递给要侦听完成的任何代码。这样就产生了非常干净的API。没有易变的状态,非常容易理解,支持所有用例。

如果坚持的话,您可以创建一个看起来像真实的代理任务:

public abstract class TransferTask
{
    public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;

    private TaskCompletionSource<object> transferTask = new ...; //changed
    private TransferStatus status;

    public TransferStatus Status
        {
            get { return this.status; }
            protected set
            {
                TransferStatus oldStatus = this.status;
                this.status = value;

                OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
            }
        }

    public Task Start(CancellationToken token)
    {
        await TransferAsync(cancellationToken);
        transferTask.SetResult(null); //complete proxy task
    }

    protected abstract Task TransferAsync(CancellationToken cancellationToken);

    protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
        {
            if (this.StatusChanged != null)
            {
                this.StatusChanged(this, txStatusArgs);
            }
        }

    public TaskAwaiter GetAwaiter()
    {
        return this.transferTask.Task.GetAwaiter(); //changed
    }
}


现在,transferTask.Task始终不为null。该任务最终将完成。我很快将其归为一体,希望这个想法很明确。

可能您应该将事件基于transferTask.Task.ContinueWith(...)

关于c# - 在同一个类上启用EAP和异步/等待,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31436445/

10-08 21:48