当您只有一个符合较小类型的变量时,如何调用重载函数?

时间:2019-07-11 20:36:18

标签: swift

Swift是否有办法正确推断给定的变量符合每个重载参数的通用协议,从而调用哪个重载函数?

在下面的代码示例中,我希望能够将类型为Command的通用对象传递给可能能够处理它的许多其他对象。

但是,当将对象下传到基本协议时,类型信息“丢失”。这使我无法根据命令的类型使用函数重载。

是否有一种方法可以“返回”该类型信息,以便Swift拥有足够的信息来正确调用适当的函数?

protocol Command {}
protocol ToDoCommand : Command {}
protocol UserCommand : Command {}

struct AddToDoCommand  : ToDoCommand {}
struct EditToDoCommand : ToDoCommand {}

struct AddUserCommand  : UserCommand {}
struct EditUserCommand : UserCommand {}

class ToDoManager {
  func performCommand(_ command:Command) {
    guard let todoCommand = command as? ToDoCommand else {
      return
    }

    // Perform some tasks that are common to all ToDoCommands...

    // This produces a compiler error because 'todoCommand' is of 
    // type ToDoCommand, which is not specific enough for Swift
    // to deduce which overloaded function to call. Can this
    // be overcome?

    performToDoCommand(todoCommand)
  }

  func performToDoCommand(_ command:AddToDoCommand) {
    print("Add ToDo")
  }

  func performToDoCommand(_ command:EditToDoCommand) {
    print("Edit ToDo")
  }
}

class UserManager {
  func performCommand(_ command:Command) {
    guard let userCommand = command as? UserCommand else {
      return
    }

    // Perform some tasks that are common to all UserCommands...

    // See note above...
    performUserCommand(userCommand)
  }

  func performUserCommand(_ command:AddUserCommand) {
    print("Add User")
  }

  func performUserCommand(_ command:EditUserCommand) {
    print("Edit User")
  }

}

let todoManager = ToDoManager()
let userManager = UserManager()

let command = AddUserCommand()
todoManager.performCommand(command)
userManager.performCommand(command)

2 个答案:

答案 0 :(得分:2)

有两种方法可以解决此问题...

使用switch重新建立类型

Swift需要在编译时知道它正在调用哪个重载函数。如果Swift在编译时不知道它具有哪种类型的变量,则不会发生这种情况。

要获取类型信息,可以使用switch重新建立类型:

func performCommand(_ command:Command) {
    guard let todoCommand = command as? ToDoCommand else {
        return
    }

    // Perform some tasks that are common to all ToDoCommands...

    switch todoCommand {
    case let command as AddToDoCommand:
        performCommand(command)
    case let command as EditToDoCommand:
        performCommand(command)
    default: break
    }
}

使用多态性

让Swift决定在运行时运行哪个performToDoCommand()命令的一种方法是使用多态性

将实现func performToDoCommand()的要求添加到ToDoCommand协议中,然后为每个符合struct的{​​{1}}实施。调用正确的一个就很简单...

ToDoCommand

答案 1 :(得分:2)

我认为您正在尝试重新创建类继承。相反,您需要合成。您已经创建了许多空的协议,它们的行为类似于抽象类。这不是考虑协议的正确方法。我认为您想要的只是结构。

// A command is just something that can be performed
struct Command {
    let perform: () -> Void
}

// And there are lots of ways to make them
extension Command {
    // We can make command types that wrap other command types
    static func makeToDo(additional: @escaping () -> Void) -> Command {
        return Command {
            // common todo behavior
            additional()
        }
    }
}

// And we can just make regular commands
extension Command {
    // Things that include ToDo
    static func makeAddToDo() -> Command { makeToDo { print("Add ToDo") } }
    static func makeEditToDo() -> Command { makeToDo { print("Edit ToDo") }}
    // Things that don't
    static func makeAddUser() -> Command { Command{print("Add User")}}
    static func makeEditUser() -> Command { Command{print("Edit User")}}
}

现在没有理由让UserManager忽略发送给它的东西,或者ToDoManager忽略发送给它的东西。那些真的使人迷惑。 “这是一个命令。如果您知道怎么做,请执行它,如果不知道,请忽略它。”如果没有经理知道如何执行该命令,您应该怎么办?还是多个经理?

相反,您只需创建一堆命令,然后调用.perform()