Inout 参数可以设置为类型不是 'CHILD' 的值,而是使用声明为类型 'PARENT' 的值

dsy*_*nkd 0 oop swift

我试图在 swift 中设计一个抽象类,其中一个函数采用该类的子类的实例。但是,我想将参数的类型指定为抽象类的名称,因为我不知道将传递该类的哪个子级。示例代码:

protocol Character {
...
}
extension Character {
...
   mutating func fight(target: inout Character) {
      target.hp -= 10
   }
}
struct Warrior: Character {
...
}
struct Mage: Character {
...
}
foo = Warrior()
bar = Mage()
foo.fight(target: bar)
Run Code Online (Sandbox Code Playgroud)

在这段代码中,我得到了错误:Inout argument could be set to a value with a type other than 'Mage', use a value declared as type 'Character' instead,这意味着编译器无法识别 Mage 实现了 Character,因此是字符类型。我该如何解决这个问题?

dan*_*dan 6

我假设您的代码如下所示:

extension Character {
    mutating func fight(target: inout Character) {
        target.hp -= 10
    }
}

var foo = Warrior()
var bar = Mage()

foo.fight(target: &bar)
Run Code Online (Sandbox Code Playgroud)

问题是您的bar变量被推断为类型Mage,您不能将其作为 inout 参数传递给采用Character.

要了解为什么存在此限制,请想象您的fight函数具有以下实现:

mutating func fight(target: inout Character) {
    target = Warrior()
}
Run Code Online (Sandbox Code Playgroud)

如果将一个类型的变量传递Mage给该函数是有效的,那么您将一个类型的值分配给一个显然无法工作的类型Warrior的变量Mage

如果要传递bar给函数,则需要将其声明为:

var bar: Character = Mage()
Run Code Online (Sandbox Code Playgroud)

另一种方法是使您的fight函数通用,它可以与您的原始调用代码一起使用,但会有其他限制:

mutating func fight<T: Character>(target: inout T) {
    target.hp -= 10
}
Run Code Online (Sandbox Code Playgroud)