[英]understanding protocol extensions in swift
我正在嘗試實現基本的協議擴展,如下所示:
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
。 為什么不起作用?
你不會寫
if a < b {
因為a
和b
類型為Value
而不是Comparable
。
但是,您可以比較與a
和b
相關的float
值
if a.get() < b.get() {
如本問答中所述 ,在作為static
成員實現的運算符重載與作為頂級函數實現的運算符重載之間存在差異。 static
成員帶有一個附加的(隱式) self
參數,編譯器需要能夠推斷出該參數。
那么, self
價值如何推論呢? 好吧,必須從重載的操作數或返回類型中完成。 對於協議擴展,這意味着這些類型之一必須是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
必須是具體的符合類型; 它不能僅僅是Value.self
(因為協議擴展中的靜態方法僅可用於調用具體的符合類型,而不能用於協議類型本身)。
通過將重載定義為頂級函數,我們可以修復上面的示例和您的示例:
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(_:_:)
函數。
如果您希望能夠創建可以使用>
, <
, ==
等運算符的類型,則它們必須符合Comparable
協議:
protocol Value: Comparable {
func get() -> Float
mutating func set(to: Float)
}
但是,這帶來了更多限制。 您必須將協議擴展中的所有Value
類型更改為Self
:
extension Value {
static func min(of a: Self, and b: Self) -> Float {
if a < b { //Expression type 'Bool' is ambiguous without more context
return a.get()
}else{
return b.get()
}
}
static func < (a: Self, b: Self) -> Bool {
return a.get() < b.get()
}
}
將Self
類型替換為實現協議的類型。 因此,如果我在類型Container
上實現Value
,則方法簽名將如下所示:
class Container: Value {
static func min(of a: Container, and b: Container) -> Float
static func < (a: Container, b: Container) -> Bool
}
附帶說明一下,如果您希望Value
符合Comparable
,則可能還需要將==
運算符添加到Value
擴展中:
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.get() < rhs.get()
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.