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)
答案 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()
。