簡體   English   中英

快速了解協議擴展

[英]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 {

因為ab類型為Value而不是Comparable

但是,您可以比較與ab相關的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM