Swift 协议扩展覆盖

Swift Protocol Extensions overriding

我正在试验 Swift 协议扩展,我发现这种行为非常令人困惑。你能帮我如何得到我想要的结果吗?

查看代码最后 4 行的注释。 (如果需要,您可以将其复制粘贴到 Xcode7 游乐场)。谢谢!!

protocol Color { }
extension Color {  var color : String { return "Default color" } }

protocol RedColor: Color { }
extension RedColor { var color : String { return "Red color" } }


protocol PrintColor {
    
     func getColor() -> String
}

extension PrintColor where Self: Color {
    
    func getColor() -> String {
        
        return color
    }
}


class A: Color, PrintColor { }
class B: A, RedColor { }


let colorA = A().color // is "Default color" - OK
let colorB = B().color // is "Red color" - OK


let a = A().getColor() // is "Default color" - OK
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

简短的回答是协议扩展不做 class 多态性。这在一定程度上是有道理的,因为协议可以被结构或枚举采用,并且因为我们不希望仅仅采用协议来引入不必要的动态调度。

因此,在getColor()中,color实例变量(可能更准确地写为self.color)并不代表你认为的那样,因为你在想class-多态性而协议不是。所以这有效:

let colorB = B().color // is "Red color" - OK

...因为您要求 class 来解析 color,但这并不符合您的预期:

let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

...因为 getColor 方法完全在协议扩展中定义。您可以通过在 B:

中重新定义 getColor 来解决问题
class B: A, RedColor {
    func getColor() -> String {
        return self.color
    }
}

现在 class 的 getColor 被调用,它对 self 是什么有一个多态的想法。

我设法通过在 Color 上定义 color 并切换 B 的实现列表来使其工作。如果 B 必须是 A 则不太好.

protocol Color {
    var color : String { get }
}

protocol RedColor: Color {

}

extension Color {
    var color : String {
        get {return "Default color"}
    }
}

extension RedColor {
    var color : String {
        get {return "Red color"}
    }
}

protocol PrintColor {
    func getColor() -> String
}

extension PrintColor where Self: Color {
    func getColor() -> String {
        return color
    }
}

class A : Color, PrintColor {

}

class B : RedColor, PrintColor {

}

let a = A().getColor() // "Default color"
let b = B().getColor() // "Red color"

这里有两个截然不同的问题:协议的动态行为和协议 "default" 实现的解析。

  1. 在动态方面,我们可以用一个简单的例子来说明问题:

    protocol Color { }
    
    extension Color {
        var color: String { return "Default color" }
    }
    
    class BlueBerry: Color {
        var color: String { return "Blue color" }
    }
    
    let berry = BlueBerry()
    print("\(berry.color)")                 // prints "Blue color", as expected
    
    let colorfulThing: Color = BlueBerry()
    print("\(colorfulThing.color)")         // prints "Default color"!
    

    正如您在 中指出的那样,如果您将 color 定义为原始 Color 协议的一部分(即,从而指示编译器合理地期望符合 classes 来实现此方法,并且仅在找到 none 时才使用协议的实现):

    protocol Color {
        var color: String { get }
    }
    
    ...
    
    let colorfulThing: Color = BlueBerry()
    print("\(colorfulThing.color)")         // now prints "Blue color", as expected
    
  2. 现在,在 中,您质疑为什么当 BA 的子class 时,这会有点崩溃。

    我认为记住协议扩展中的方法实现是 "default" 实现是有帮助的,即如果符合 class 本身不实现它时要使用的实现。您的案例中混淆的根源来自 B 符合 RedColor 的事实,它具有 color 的默认实现,但 B 也是一个 subclass 的 A 符合 Color,它具有 color.

    的不同默认实现

    所以,我们可能会质疑 Swift 对这种情况的处理(就我个人而言,我宁愿看到关于这种本质上模棱两可的情况的警告),但在我看来,问题的根源是有两个不同的层次结构(子 classes 的 OOP 对象层次结构和协议继承的 POP 协议层次结构),这导致两个相互竞争的 "default" 实现。

我知道这是一个老问题,所以您可能早就转向其他问题了,这很好。但是,如果您仍在为重构此代码的正确方法而苦苦挣扎,请分享一些有关此 class 层次结构以及此协议继承实际代表的内容的信息,我们可能会提供更具体的建议。这是抽象示例进一步混淆问题的情况之一。让我们看看 types/protocols 到底是什么。 (如果您有工作代码,http://codereview.stackexchange.com 可能是更好的选择。)

我在尝试通过协议实现 "optional" 方法时遇到了这个问题。它 可以 在结构中,在不继承的 class 中,以及从实现非继承的基础继承的 class 中工作-protocol-default 方法可以被覆盖。唯一不起作用的情况是 class 继承自声明一致性但不提供其自己的 "non-default" 实现的基础 - 在这种情况下,协议扩展的默认值为 "baked-in"到基础 class,并且不能被覆盖或重新定义。

简单示例:

typealias MyFunction = () -> ()
protocol OptionalMethod {
    func optionalMethod() -> MyFunction?
    func executeOptionalMethod()
}
extension OptionalMethod {
    func optionalMethod() -> MyFunction? { return nil }
    func executeOptionalMethod() {
        if let myFunc = self.optionalMethod() {
            myFunc()
        } else {
            print("Type \(self) has not implemented `optionalMethod`")
        }
    }
}

class A: OptionalMethod {
}
class B: A {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optional method") }
    }
}
struct C: OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optionalMethod") }
    }
}
class D: OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optionalMethod") }
    }
}
class E: D {
    override func optionalMethod() -> MyFunction? {
        return { print("Hello DIFFERENT optionalMethod") }
    }
}
/* Attempt to get B to declare its own conformance gives:
// error: redundant conformance of 'B2' to protocol 'OptionalMethod'
class B2: A, OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optional method") }
    }
}
*/
class A2: OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return nil
    }
}
class B2: A2 {
    override func optionalMethod() -> MyFunction? {
        return { print("Hello optionalMethod") }
    }
}

let a = A() // Class A doesn't implement & therefore defaults to protocol extension implementation
a.executeOptionalMethod() // Type __lldb_expr_201.A has not implemented `optionalMethod`
let b = B() // Class B implements its own, but "inherits" implementation from superclass A
b.executeOptionalMethod() // Type __lldb_expr_205.B has not implemented `optionalMethod`
let c = C() // Struct C implements its own, and works
c.executeOptionalMethod() // Hello optionalMethod
let d = D() // Class D implements its own, inherits from nothing, and works
d.executeOptionalMethod() // Hello optionalMethod
let e = E() // Class E inherits from D, but overrides, and works
e.executeOptionalMethod() // Hello DIFFERENT optionalMethod
let a2 = A2() // Class A2 implements the method, but returns nil, (equivalent to A)
a2.executeOptionalMethod() // Type __lldb_expr_334.A2 has not implemented `optionalMethod`
let b2 = B2() // Class B2 overrides A2's "nil" implementation, and so works
b2.executeOptionalMethod() // Hello optionalMethod

注意:建议的解决方案 "Defining color as part of the original Color protocol" 无法解决涉及继承的问题,例如 RedBerry继承自BlueBerry,符合协议Color

protocol Color {
    var color: String { get }
}

extension Color {
    var color: String { return "Default color" }
}

class BlueBerry: Color {
    //    var color: String { return "Blue color" }
}

class RedBerry: BlueBerry {
    var color: String { return "Red color" }
}

let berry = RedBerry()
print(berry.color)             // Red color

let colorfulThing: Color = RedBerry()
print(colorfulThing.color)     // Actual: Default color, Expected: Red color