1

在我的 VB.NET 程序中,我正在读取来自连接到串行端口的设备的命令响应。目前命令集是固定的,但将来可能会添加新命令。有没有比我可以用来读取每个命令并为它们创建对象的巨大 switch/if-then-else 语句更优雅的东西?我有一个基本的“命令”类,但派生了每个命令拥有的特殊功能。

我希望做出一个优雅的解决方案,避免每次将新对象添加到命令集中时更新巨大的 switch 语句。

4

1 回答 1

4

这取决于线路上消息的格式以及反序列化它们的方式。我会做这样的事情(示例是在 C# 中,因为我不使用 VB.NET,但它应该很容易转换)。

每个命令都实现ICommand接口,并从某个CommandBase实现类派生。CommandBase定义MessageId每个命令类型唯一的抽象属性(您也可以使用常量)。此 ID 也是线路上消息头的一部分,以便您知道来自设备的命令。

现在您从设备获取消息 ID:

int msgId = ... // what came from the device
Type cmdType = GetTypeForMessage(msgId); // get the corresponding implementation
ICommand cmd = (Command)Activator.CreateInstance(cmdType); // crate an instance
cmd.Deserialize(buffer); // or whatever way you do the serialization
cmd.Execute();  // run the command

您可以从之前设置的地图中获得正确的类型:

Type GetTypeForMessage(int msgId) {
    // m_commandMap is Dictionary<int, Type>
    return m_commandMap[msgId];
}

现在剩下的问题是如何设置m_commandMap。一种方法是自动注册从某个类派生的所有CommandBase类。你在启动时做这样的事情:

  // find all types in this assembly
  Assembly assembly = Assembly.GetExecutingAssembly();
  foreach (var type in assembly.GetTypes()) {
    if(typeof(CommandBase).IsAssignableFrom(type)) { // which derive from CommandBase
      CommandBase cmd = (CommandBase) Activator.CreateInstance(type);  
      m_commandMap[cmd.MessageId] = type;
      // I would make MessageId a static constant on class and read it
      // using reflection, so I don't have to instantiate an object
    }                    
  }

现在,当您需要实现新命令时,您所要做的就是定义它:

class NewCommand : CommandBase {
    public override int MessageId { get { return 1234; } }
    // or preferably: public const int MessageId = 1234;
    // the rest of the command: ...
}

如果对应的ID来自设备,它将在启动时自动注册并用于反序列化。

于 2013-03-12T10:00:39.993 回答