Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

第3章:协议扩展(Protocol Extensions)

3.3 使用 where 子句约束扩展

协议扩展的强大之处不仅在于提供默认实现,还在于其可以通过 where 子句添加条件约束,使扩展仅适用于满足特定条件的遵循者。这种机制让协议扩展能够更精确地针对不同场景提供实现,避免“一刀切”的默认行为,同时保持类型安全和灵活性。本节将详细探讨 where 子句在协议扩展中的用法、适用场景和设计技巧,并通过示例展示其实际应用。

什么是 where 子句?

在 Swift 中,where 子句用于在泛型、协议扩展或条件语句中指定约束条件。在协议扩展中,where 子句可以限制扩展的适用范围,只有当遵循者满足条件时,扩展中的实现才会生效。基本语法如下:

extension ProtocolName where Condition {
    // 仅在满足 Condition 时生效的实现
}

条件可以基于类型、协议遵循或其他属性。例如:

protocol Measurable {
    var value: Double { get }
}

extension Measurable where Self: Equatable {
    func isEqual(to other: Self) -> Bool {
        return self.value == other.value
    }
}

在这个例子中,isEqual(to:) 仅对同时遵循 Measurable 和 Equatable 的类型生效。

使用 where 子句的场景

where 子句适用于需要根据遵循者的特性提供差异化实现的场景。常见用法包括:

  1. 约束类型特性:要求遵循者遵循其他协议。
  2. 约束属性类型:限制属性或关联类型的具体类型。
  3. 条件逻辑:根据特定条件启用扩展功能。

示例 1:基于协议约束

假设我们有一个表示“可比较”对象的协议,想为支持相等性比较的类型提供默认实现:

protocol ComparableItem {
    var score: Int { get }
    func isGreaterThan(_ other: Self) -> Bool
}

extension ComparableItem {
    func isGreaterThan(_ other: Self) -> Bool {
        return score > other.score
    }
}

extension ComparableItem where Self: Equatable {
    func isEqualTo(_ other: Self) -> Bool {
        return self == other
    }
}

struct Player: ComparableItem, Equatable {
    var score: Int
    
    static func == (lhs: Player, rhs: Player) -> Bool {
        return lhs.score == rhs.score
    }
}

let player1 = Player(score: 10)
let player2 = Player(score: 10)
print(player1.isGreaterThan(player2)) // 输出: false
print(player1.isEqualTo(player2))     // 输出: true
  • 默认实现 isGreaterThan(_:) 对所有 ComparableItem 生效。
  • isEqualTo(_:) 仅对遵循 Equatable 的类型(如 Player)可用。

如果一个类型不遵循 Equatable,则无法访问 isEqualTo(_:):

struct Item: ComparableItem {
    var score: Int
}

// item.isEqualTo() // 编译错误:Item 未遵循 Equatable

示例 2:基于属性类型约束

假设协议中有个属性,我们想为特定类型提供额外功能:

protocol Configurable {
    var config: Any { get }
    func applyConfig()
}

extension Configurable {
    func applyConfig() {
        print("Applying config: \(config)")
    }
}

extension Configurable where config is String {
    func configLength() -> Int {
        return (config as! String).count
    }
}

struct StringConfig: Configurable {
    var config: String
}

struct IntConfig: Configurable {
    var config: Int
}

let stringConfig = StringConfig(config: "settings")
stringConfig.applyConfig()     // 输出: Applying config: settings
print(stringConfig.configLength()) // 输出: 8

let intConfig = IntConfig(config: 42)
intConfig.applyConfig()        // 输出: Applying config: 42
// intConfig.configLength()    // 编译错误:config 不是 String
  • applyConfig() 是通用的默认实现。
  • configLength() 仅对 config 类型为 String 的遵循者生效。

示例 3:结合关联类型约束

对于带有关联类型的协议,where 子句可以约束关联类型。例如:

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}

extension Container where Item: Equatable {
    func contains(_ item: Item) -> Bool {
        return items.contains(item)
    }
}

struct StringContainer: Container {
    var items: [String]
}

struct IntContainer: Container {
    var items: [Int]
}

let strings = StringContainer(items: ["a", "b", "c"])
print(strings.contains("b")) // 输出: true

let ints = IntContainer(items: [1, 2, 3])
print(ints.contains(2))      // 输出: true
  • contains(_:) 仅对 Item 遵循 Equatable 的容器生效。
  • 因为 String 和 Int 都遵循 Equatable,两个容器都能使用该方法。

设计技巧与注意事项

  1. 精确性:使用 where 子句时,确保约束条件清晰,避免过于宽泛或复杂的逻辑。
  2. 类型安全:where 子句保证了类型匹配,编译器会在不满足条件时报错。
  3. 优先级:如果多个扩展为同一方法提供实现,Swift 会选择最具体的实现(即约束最严格的)。
    extension Container {
        func count() -> Int { return items.count }
    }
    
    extension Container where Item == String {
        func count() -> Int { return items.count * 2 } // 更具体
    }
    
    let strContainer = StringContainer(items: ["a", "b"])
    print(strContainer.count()) // 输出: 4
    
  4. 可读性:复杂的 where 子句可能降低代码可读性,建议拆分为多个简单扩展。

小结

通过 where 子句,协议扩展可以根据遵循者的特性提供条件化的默认实现。这种机制增强了扩展的精确性和适用性,让开发者能够为特定场景定制行为,同时保持协议的通用性。下一节将探讨协议扩展与方法分派的底层机制,进一步揭示其工作原理和性能特性。


Last Updated:: 3/10/25, 2:58 PM