swift - 快速理解协议(protocol)扩展

标签 swift protocols protocol-extension

我正在尝试像这样实现一个基本的协议(protocol)扩展:

protocol Value {
    func get() -> Float
    mutating func set(to:Float)
}
extension Value {
    static func min(of a:Value, and b:Value) -> Float {
        if a < b { //Expression type 'Bool' is ambiguous without more context
            return a.get()
        }else{
            return b.get()
        }
    }
    static func < (a:Value, b:Value) -> Bool {
        return a.get() < b.get()
    }
}

if 子句中,编译器说:Expression type 'Bool' is ambiguous without more context。为什么这行不通?

最佳答案

如前所述in this Q&A ,实现为 static 的运算符重载之间存在差异成员和运算符重载实现为顶级函数。 static成员额外(隐式)self编译器需要能够推断出的参数。

那么self的值是多少呢?推断?好吧,它必须从重载的操作数或返回类型来完成。对于协议(protocol)扩展,这意味着其中一种类型需要是 Self .请记住,您不能直接调用类型上的运算符(即您不能说 (Self.<)(a, b) )。

考虑以下示例:

protocol Value {
  func get() -> Float
}

extension Value {
  static func < (a: Value, b: Value) -> Bool {
    print("Being called on conforming type: \(self)")
    return a.get() < b.get()
  }
}

struct S : Value {
  func get() -> Float { return 0 }
}

let value: Value = S()
print(value < value) // Ambiguous reference to member '<'

self 的值是多少?在调用 < ?编译器无法推断它(我真的认为它应该直接在重载上出错,因为它是不可调用的)。请记住 self在协议(protocol)扩展中的静态范围内必须是具体符合类型;它不能只是Value.self (因为协议(protocol)扩展中的静态方法仅可用于调用具体的符合类型,而不是协议(protocol)类型本身)。

我们可以通过将重载定义为顶级函数来修复上面的示例和您的示例:

protocol Value {
  func get() -> Float
}

func < (a: Value, b: Value) -> Bool {
  return a.get() < b.get()
}

struct S : Value {
  func get() -> Float { return 0 }
}

let value: Value = S()
print(value < value) // false

这是可行的,因为现在我们不需要推断 self 的值.

我们还可以为编译器提供一种方法来推断 self 的值,通过使一个或两个参数取 Self :

protocol Value {
  func get() -> Float
}

extension Value {
  static func < (a: Self, b: Self) -> Bool {
    print("Being called on conforming type: \(self)")
    return a.get() < b.get()
  }
}

struct S : Value {
  func get() -> Float { return 0 }
}

let s = S()
print(s < s)

//  Being called on conforming type: S
//  false

编译器现在可以推断出 self来自静态类型的操作数。但是,如上所述,这需要是具体类型,因此您不能处理异构 Value操作数(您可以使用一个操作数采用 Value ;但不能同时使用两个,因为那样就无法推断出 self )。


但请注意,如果您提供 < 的默认实现,您可能还应该提供 == 的默认实现.除非你有充分的理由不这样做,否则我还建议你让这些重载采用同质的具体操作数(即 Self 类型的参数),这样它们就可以为 Comparable 提供默认实现。 .

也不是 get()set(to:)要求,我建议改用可设置的属性要求:

// Not deriving from Comparable could be useful if you need to use the protocol as
// an actual type; however note that you won't be able to access Comparable stuff,
// such as the auto >, <=, >= overloads from a protocol extension.
protocol Value {
  var floatValue: Double { get set }
}

extension Value {

  static func == (lhs: Self, rhs: Self) -> Bool {
    return lhs.floatValue == rhs.floatValue
  }

  static func < (lhs: Self, rhs: Self) -> Bool {
    return lhs.floatValue < rhs.floatValue
  }
}

最后,如果Comparable一致性对于符合 Value 至关重要, 你应该让它派生自 Comparable :

protocol Value : Comparable {
  var floatValue: Double { get set }
}

你不应该需要 min(of:and:)在任何一种情况下都起作用,因为当符合类型符合 Comparable 时, 它可以使用顶级 min(_:_:)功能。

关于swift - 快速理解协议(protocol)扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47931606/

相关文章:

swift - swift 中的闭包操作

ios - 在 NSObject 上使用类别来提供默认协议(protocol)实现有什么问题?

ios - 可以动态地将逻辑附加到方法吗?

ios - 调用协议(protocol)扩展中的方法而不是 View Controller 中的方法实现

ios - 获取昨天的 NSDate 开始和结束

ios - uploadTaskWithRequest 后台 session fromFile 崩溃

python - 手动发送电子邮件 - Python

swift - 带有空初始化的协议(protocol)

xcode - 此应用程序正在从后台线程修改自动布局引擎,这可能会导致引擎损坏

ios - Swift - 使用协议(protocol)和委托(delegate)将值传递回 ViewController