我有一个 Operation
类,像这样...
public sealed class Operation
{
public void DoSomething1(ArgType1 arg) {...}
public void DoSomething2(ArgType2 arg) {...}
...
public Task<bool> Execute() {...}
}
DoSomething
方法打包需要完成的工作,存储参数并且Execute()
方法将启动一个Task
以原子方式完成这项工作。 DoSomething
的主要影响是副作用,但其中一些也有意义返回值,我的第一反应是返回一个Task
,如下所示...
public Task<ResultType3> DoSomething3(ArgType3 arg) {...}
但问题是,这个Task
不像大多数任务一样是“实时”的。在调用Execute()
以启动工作之前,等待该Task
的结果将是没有意义的,因此我认为这会让消费者感到困惑。好像DoSomething3()
和Execute()
的返回值是相互依赖的任务。
我可以将Task<>
包装在一个新类型中,称为Result<>
,并在内部它将保存一个Task<>
,而Operation
将保留其TaskCompletionSource<>
并在Execute()
结束时设置Result
,以便客户端在等待由Execute()
返回的Task
后可以观察到Result
。
public Result<T>
{
internal Result(Task t) { _t = t; }
public bool IsComplete { get { return _t.IsComplete; } }
public T Result { get { return _t.Result; } }
// Perhaps more methods delegating to the underlying Task
}
public Result<ResultType4> DoSomething4(ArgType4 arg) {...}
包装Task
的主要动机是向消费者传达DoSomething3()
的结果不是实时Task
,并使其难以/不可能调用...
var result = await op.DoSomething4(x);
由于没有人启动操作
,因此这可能会导致代码死锁。请注意,这个Result<>
类型与不同语义的Nullable<>
相似。
另一种方法是让该方法返回某些不透明对象,该对象将用作从Execute()
完成后检索实际结果的键...
var token = op.DoSomething4(x);
...
var succeeded = await op.Execute();
if (! succeeded) return;
var result = op.RetrieveResult(token);
“Retrieve result” 的签名类似于……
public T RetrieveResult(Token<T> token) {...}
我认为另一个选择是添加一个额外的参数作为回调函数,在 Execute()
结束时执行,当实际结果可用时...
public void DoSomething5(ArcType5 arg, Func<ResultType5,Task> callback) {...}
正如您所看到的,我有几个不同的选项,但是并没有一个强烈的直觉告诉我哪个最合适。不幸的是,这可能主要是品味问题,但我希望能够得到有关不同方法的反馈。
DoSomethingN
返回东西是绝对必要的吗?例如,您不能返回一个由Execute
(立即)返回的Result
类,该类具有与每个DoSomethingN
的结果相对应的Task
成员吗?然后,您的消费代码可能如下所示:op.EnqueueWork1(someDataForWork1); op.EnqueueWork1(someDataForWork2); var result = op.Execute(); var work2Result = await result.Work2Results;
- Asad SaeeduddinDoWhatever
方法中返回值是否绝对必要?你可以将它们全部设置为空(void),并让Execute
返回一个类型实例,该实例公开表示一些排队工作对应的每个子结果的Task<SomeData>
,以及用于整体完成的Task<bool>
。 - Asad Saeeduddin