- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我在设置命令处理架构时遇到了一些问题。我希望能够创建许多从 ICommand 派生的不同命令;然后,创建许多从 ICommandHandler 派生的不同命令处理程序;
这是我开始定义的接口(interface)和类:
interface ICommand {}
class CreateItemCommand : ICommand {}
interface ICommandHandler<TCommand> where TCommand : ICommand {
void Handle(TCommand command);
}
class CreateItemCommandHandler : ICommandHandler<CreateItemCommand> {
public void Handle(CreateItemCommand command) {
// Handle the command here
}
}
我有一个帮助程序类可以创建适当类型的命令:
class CommandResolver {
ICommand GetCommand(Message message) {
return new CreateItemCommand(); // Handle other commands here
}
}
还有一个创建适当处理程序的辅助类;这是我遇到麻烦的地方:
class CommandHandlerResolver {
public ICommandHandler<TCommand> GetHandler<TCommand>(TCommand command) {
// I'm using Ninject and have an instance of an IKernel
// The following code throws an exception despite having a proper binding
// _kernel.GetService(typeof(ICommandHandler<TCommand>))
var bindingType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
var handler = _kernel.GetService(bindingType);
return handler as ICommandHandler<TCommand>;
// handler will be null after the cast
}
}
主要运行方法如下
CommandResolver _commandResolver;
HandlerResolver _handlerResolver;
void Run() {
// message is taken from a queue of messages
var command = _commandResolver.GetCommand(message);
var handler = _handlerResolver.GetHandler(command);
// handler will always be null
handler.Handle(command);
}
我可以想出几种不同的方法来重构代码,我确信这些方法可以避免这个问题,但我发现自己对这个问题有点困惑,想更多地了解发生了什么。
这个设计看起来应该可行。
最佳答案
您的问题是您混合了静态类型和运行时类型:您正在编写依赖于已构造泛型类型的代码,但随后您使用基本接口(interface)类型调用它。
让我们跟随您的主要流程:
你的 CommandResolver
总是返回静态类型 ICommand
.当你说:
var command = _commandResolver.GetCommand(message);
var handler = _handlerResolver.GetHandler(command);
command
的类型绑定(bind)到 ICommand
然后传递给 GetHander
,调用 GetHandler<ICommand>
.即 TCommand
在此调用中始终绑定(bind)到 ICommand
.
这是这里的主要问题。自 TCommand
是总是 ICommand
,做:
_kernel.GetService(typeof(ICommandHandler<TCommand>))
...不工作(它寻找 ICommandHandler<ICommand>
而内核没有);即使它确实有效,您也必须将其返回为 ICommandHandler<ICommand>
因为那是方法的返回类型。
调用 GetHandler
在不知道(在编译时)命令的真实类型的情况下,您失去了有效使用泛型的能力并且 TCommand
变得毫无意义。
因此,您尝试解决这个问题:您的解析器使用命令的运行时类型( command.GetType()
) 反射式构造类型 ICommandHandler<SomeCommandType>
并尝试在内核中找到那个。
假设您已经为该类型注册了一些东西,您将得到一个 ICommandHandler<SomeCommandType>
,然后您将尝试将其转换为 ICommandHandler<ICommand>
(请记住 TCommand
绑定(bind)到 ICommand
)。这当然行不通,除非TCommand
在 ICommandHandler<TCommand>
中声明协变 ,因为您正在向上类型层次结构;但即使它做到了,那也不是你想要的,因为你会用 ICommandHandler<ICommand>
做什么?无论如何?
简单地说:您不能使用 ICommandHandler<SomeCommand>
到 ICommandHandler<ICommand>
因为这意味着你可以传递任何类型的 ICommand
它会很高兴地处理它——这是不正确的。如果您想使用泛型类型参数,则必须在整个流程中将它们绑定(bind)到实际命令类型。
这个问题的一个解决方案是保留 TCommand
在命令和命令处理程序的整个解析过程中绑定(bind)到真实的命令类型,例如通过类似 FindHandlerAndHandle<TCommand>(TCommand command)
的东西并使用命令的运行时类型通过反射调用它。但这又臭又笨拙,并且有一个很好的理由:你在滥用泛型。
通用类型参数旨在帮助您在编译时知道您想要的类型,或者您可以将其与另一个类型参数统一的内容。在这些情况下,您不知道运行时类型,尝试使用泛型只会妨碍您。
解决这个问题的一种更简洁的方法是,当你知道命令的类型时(当你为它编写一个处理程序时)将上下文与你不知道它的上下文(当你试图为一个命令找到一个处理程序时)分开通用命令)。做到这一点的一个好方法是使用“非类型化接口(interface),类型化基类”模式:
public interface ICommandHandler // Look ma, no typeparams!
{
bool CanHandle(ICommand command);
void Handle(ICommand command);
}
public abstract class CommandHandlerBase<TCommand> : ICommandHandler
where TCommand : ICommand
{
public bool CanHandle(ICommand command) { return command is TCommand; }
public void Handle(ICommand command)
{
var typedCommand = command as TCommand;
if (typedCommand == null) throw new InvalidCommandTypeException(command);
Handle(typedCommand);
}
protected abstract void Handle(TCommand typedCommand);
}
这是连接通用和非通用世界的常用方法:调用它们时使用非通用接口(interface),但在实现时利用通用基类。您的主要流程现在如下所示:
public void Handle(ICommand command)
{
var allHandlers = Kernel.ResolveAll<ICommandHandler>(); // you can make this a dependency
var handler = allHandlers.FirstOrDefault(h => h.CanHandle(command));
if (handler == null) throw new MissingHandlerException(command);
handler.Handle(command);
}
从命令的实际运行时类型不必与处理程序的类型一一匹配的意义上说,这也更加稳健,所以如果你有一个 ICommandHandler<SomeBaseCommandType>
它可以处理 SomeDerivedCommandType
类型的命令,因此您可以为命令类型层次结构中的中间基类构建处理程序,或使用其他继承技巧。
关于c# - 逆变?协方差?这种通用架构有什么问题......?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23810372/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!