我有一个适用于硬件设备的课程。该设备支持许多命令,我想实现一个通用的 SendCommand 功能。命令可能有也可能没有输入参数和/或输出结果。
我能做的是编写一个抽象的命令类型类和一些派生的命令类型类。那些派生类实际上与命令的输入/输出细节不同。
现在我希望SendCommand
返回Task<SpecificCommandType>
,即派生类的任务,但使用当前设计我只能返回Task<BaseCommandType>
。
我将用简单的骨架代码解释:
类:
public abstract class BaseCommandType { ... }
public class CommandType1 : BaseCommandType {
TaskCompletionSource<CommandType1> Tcs;
}
public class CommandType2 : BaseCommandType {
TaskCompletionSource<CommandType2> Tcs;
}
我的功能:
public Task<T> SendCommand<T>(BaseCommandType type) where T : BaseCommandType {
...
// if I implement TaskCompletionSource<BaseCommandType> Tcs
// in abstract class, then I can return type.Tcs.Task, and remove
// generics.
// But how can I return Task<T>?
}
我打算像这样使用这个功能:
CommandTypeX cmd = new CommandTypeX(...);
SendCommand<CommandTypeX>(cmd).ContinueWith(t => {
// access some specifics of t.Result as CommandTypeX
});
我应该如何设计我的课程以便能够返回Task<CommandTypeX>
?
还是有更好的方法来做我需要的事情(没有垂头丧气)?
UPDATE1 : 更准确地说,我可以这样做,同时投降(可以做我,不是吗?):
public abstract class BaseCommandType {
public TaskCompletionSource<BaseCommandType> Tcs;
}
public class CommandTypeX : BaseCommandType { }
public Task<BaseCommandType> SendCommand(BaseCommandType type) {
...
return type.Tcs.Task;
}
// when task finishes:
type.Tcs.SetResult(type); // where type is actually of CommandTypeX
// usage:
CommandTypeX cmd = new CommandTypeX(...);
SendCommand(cmd).ContinueWith(t => {
CommandTypeX command = t.Result as CommandTypeX;
if (command != null) ...
});
但这正是我想要首先避免的。
UPDATE2 : 我想我找到了另一种方式,但对我来说仍然不太好。
public abstract class BaseCommandType {
internal abstract void SetTcs<T>(TaskCompletionSource<T> tcs);
internal abstract void HandleData(byte[] data);
}
public class CommandType1 : BaseCommandType {
private TaskCompletionSource<CommandType1> _tcs1 = new TaskCompletionSource<CommandType1>();
public string Data1;
internal override void SetTcs<T>(TaskCompletionSource<T> tcs)
{
_tcs1 = tcs as TaskCompletionSource<CommandType1>;
}
internal override void HandleData(byte[] data)
{
// Data1 = someFuncOn(data)
_tcs1.TrySetResult(this);
}
}
public class CommandType2 : BaseCommandType {
private TaskCompletionSource<CommandType2> _tcs2 = new TaskCompletionSource<CommandType2>();
public int[] Data2;
internal override void SetTcs<T>(TaskCompletionSource<T> tcs)
{
_tcs2 = tcs as TaskCompletionSource<CommandType2>;
}
internal override void HandleData(byte[] data)
{
// Data2 = someFuncOn(data)
_tcs2.TrySetResult(this);
}
}
public class Device {
private List<BaseCommandType> _commandList = new List<BaseCommandType>();
public Task<T> SendCommand<T>(T t) where T : BaseCommandType
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
t.SetTcs<T>(tcs);
_commandList.Add(t);
// later in other thread then device answers
// locate command in list
// BaseCommandType c = _commandList[some index];
// _commandList.RemoveAt(some index);
// c.HandleData(null);
return tcs.Task;
}
}
// usage like this:
CommandType2 command = new CommandType2();
device.SendCommand<CommandType2>(command).ContinueWith(t =>
{
CommandType2 command2 = t.Result;
// use command2.Data2 here;
});
这比update1更好吗?至少我可以隐藏库中的构建逻辑,所以外部的一切都是类型安全和健壮的。 或者我该如何进一步改进呢?
答案 0 :(得分:0)
我不确定我是否理解它,但是......
public abstract class BaseCommandType<T>
{
public abstract TaskCompletionSource<T> Tcs { get; }
}
public class CommandType1 : BaseCommandType<CommandType1>
{
}
public class CommandType2 : BaseCommandType<CommandType2>
{
}
public Task<T> SendCommand<T>(T type) where T : BaseCommandType<T>
{
return type.Tcs.Task;
}
修改强>
如果你不能拥有通用输入参数,那么在BaseCommandType上定义抽象beahvior而不是向下转换定义,你可以在ContinueWith方法中调用它,并在命令中覆盖它。如果您不了解输入类型,则无法将其设为通用。
答案 1 :(得分:0)
我对你的问题不了解的事情:
SendCommand<T>(BaseCommandType)
的参数类型?如果你改为使用SendCommand<T>(T)
方法,哪些方法不起作用?ContinueWith()
? await
会更容易,更具表现力。暂时忽略这些,我希望你能做到这样的事情:
public async Task<T> SendCommand<T>(BaseCommandType type) where T : BaseCommandType
{
return await type.Tcs.Task as T;
}
就个人而言,我会全力以赴。但是你说你不能出于任何原因,所以你必须在某个时候施展。使用该约束不可能简单地避免铸造。以上似乎是最简单,最有用的方法。
如果以上内容无法满足您的需求,请通过添加一个显示所有设计限制和要求的好Minimal, Complete, and Verifiable code example来改进问题,您尝试了什么,并解释您尝试过的原因不符合你的目标。