【问题标题】:How can I use a class extension to override a protocol extension?如何使用类扩展来覆盖协议扩展?
【发布时间】:2026-01-08 00:15:02
【问题描述】:

假设我有一个颜色模型:

protocol Color {
    var value: String? { get }
}

class UnknownColor: Color {
    let value: String? = nil
}

class KnownColor: Color {
    let value: String?

    init(value: String? = nil) {
        self.value = value
    }
}

在我的视图文件中,我向我的颜色模型添加了一些视图细节。 这些细节不是特定于模型的,而是特定于视图的。

fileprivate extension Color {
    fileprivate var representation: String {
        return self.value!
    }
}

fileprivate extension UnknownColor {
    fileprivate var representation: String {
        return "#000"
    }
}

现在,当我在视图文件中使用我的颜色模型时,我希望我的 UnknownColors 将自己表示为 "#000",但当 UnknownColor 被转换为 Color 时,情况并非如此。

let color1 = KnownColor()
let color2 = KnownColor(value:"#fff")
let color3 = UnknownColor()

color1.representation  // Fatal error  (GOOD)
color2.representation  // "#fff"  (GOOD)
color3.representation  // "#000" (GOOD)

if let color = color3 as? Color {
    color.representation // Fatal error  (BAD, expected "#000")
}

我想避免对UnknownColor 进行公然的类型检查,所以这样的解决方案并不理想:

func colorRepresentation(_ color: Color) {
    if let color = color as? UnknownColor {
        return "#000"
    } else {
        return color.value!
    }
}

我想不惜一切代价避免对颜色模型进行进一步更改。

Color 协议的许多实现可能想要使用Color.representation,因此将extension Color 更改为extension KnownColor 不是一种选择。

有没有一种方法可以重组我的代码,以便在 Color 实际上是 UnknownColor 时使用 UnknownColor.representation

【问题讨论】:

  • 我找到了这个链接,其中包含有关该主题的更多信息:medium.com/ios-os-x-development/…
  • 您现在已经从根本上改变了您的问题,但没有明确表示您已经使用该更改更新了您的问题。因为它,我现在的回答没有任何意义。如果您要执行此类操作,请明确说明您已通过在 UPDATE 部分中进行更改来更改问题,因此阅读此主题的人将能够理解所讨论的内容。
  • 对不起,我认为我在您的回答下的评论足以说明这一点。我的问题有问题,我已将其更新为正确。

标签: swift swift-protocols swift-extensions swift-class


【解决方案1】:

我将您的代码直接复制并粘贴到 Playground 中,它运行良好(color3.representation == "#000")。

扩展名是否在单独的文件中?如果是这样,fileprivate 关键字将使它们对类不可见。

作为参考,这是我放入 Playground 的全部代码:

protocol Color {
    var value: String? { get }
}

class UnknownColor: Color {
    let value: String? = nil
}

class KnownColor: Color {
    let value: String?

    init(value: String? = nil) {
        self.value = value
    }
}

fileprivate extension Color {
    fileprivate var representation: String {
        return self.value!
    }
}

fileprivate extension UnknownColor {
    fileprivate var representation: String {
        return "#000"
    }
}

let color1 = KnownColor()
let color2 = KnownColor(value:"#fff")
let color3 = UnknownColor()

//color1.representation  // Fatal error  (GOOD)
color2.representation  // "#fff"  (GOOD)
color3.representation  // Fatal error  (BAD, expected "#000")

【讨论】:

  • 您好 Nima 感谢您试一试。扩展名在另一个文件中,如果“表示”方法仅在该文件中可用,我会更喜欢。也许还有另一种方法可以在没有 fileprivate 的情况下实现这一目标?
  • 好吧,这很奇怪,我按照你描述的方式尝试了——一个文件中的颜色协议和类,另一个文件中的扩展名——我可以访问另一个文件中的 color3.representation文件,没问题。这是我为扩展/使用尝试的代码:gist.github.com/nyousefi/4d69f7fac5443a7b9bd48043e0e4f7f1
  • 啊,我想我在这里看到了不同之处。在所写的问题中,color3 被定义为UnknownColor。但是,如果我们将 color3 转换为 Color,我们就会得到我所看到的行为。我会更新问题