2016-11-21 93 views
2

我想提高我對泛型的知識,並且遇到了一個我無法解決的問題。我有兩種不同的類型(IntDouble)。這兩種類型都實現了功能advanced(by:)。我想製作一個通用函數來調用給定類型的advanced(by:)實現通用接口的類型的通用函數

好處是我可以用替代intAdvancedByTen(value:)doubleAdvancedByTen(value:)

這裏是我的遊樂場:

let myDouble: Double = 1 
let myInt: Int = 2 

func intAdvanceByTen(value: Int) {  // Replace this function 
    value.advanced(by: 10) 
} 

func doubleAdvanceByTen(value: Int) { // ...and this function 
    value.advanced(by: 10) 
} 

protocol CanAdvance { 
    func advance(_ by: Any) 
} 
             // ...with this generic function 
func genericAdvanceByTen(value: CanAdvance) { 
    value.advance(10) 
} 

genericAdvanceByTen(value: myInt)  // Error: Argument "Int" does not conform to expected type "CanAdvance" 

如何讓通用函數知道,通過類型實現了advanced(by:)方法?

回答

1

試試這個:

protocol CanAdvance { 
    // This method is intentionally designed to have the same signature as the 
    // methods built into Int and Double 
    func advanced(by: Self) -> Self 

    // We need this primarily for the definition of the constant 10. The built 
    // in `advanced` function requires the distance to be of the same type. 
    // 
    // The conversion syntax in Swift is via init: 
    //  let aDouble = Double(anInt) 
    // Not the C-like cast: 
    //  let aDouble = anInt as! Double // invalid 
    // 
    // Hence we must have a way to convert 10 to the appropriate Int or Double. 
    // Conveniently, both types can convert from an Int32 so we put this 
    // requirement in the protocol 
    init(_ value: Int32) 
} 

extension Int : CanAdvance { } 
extension Double : CanAdvance { } 

func genericAdvanceByTen<T: CanAdvance>(value: T) -> T { 
    let distance = T(10) 
    return value.advanced(by: distance) 
} 

genericAdvanceByTen(value: 2)  // 12 
genericAdvanceByTen(value: 3.14) // 13.14 
+0

偉大的作品,我真的很高興。謝謝! :)我很困惑,因爲這是我第一次看到使用大寫'Self',所以這裏有一個鏈接來解釋'self'和'Self'之間的區別:http://stackoverflow.com/questions/27863810/distinction -in-SWIFT之間大寫自和小寫自 – Andrej

1

不需要定義自己的協議 - advanced(by:)由標準libary的Strideable協議規定:

public protocol Strideable : Comparable { 

    /// A type that can represent the distance between two values of `Self`. 
    associatedtype Stride : SignedNumber 

    // ... 

    /// Returns a `Self` `x` such that `self.distance(to: x)` approximates `n`. 
    /// 
    /// If `Stride` conforms to `Integer`, then `self.distance(to: x) == n`. 
    /// 
    /// - Complexity: O(1). 
    public func advanced(by n: Self.Stride) -> Self 
} 

因此,你只是想限制你的函數取輸入爲Strideable

鑑於Stride相關聯的類型(什麼advanced(by:)預計作爲參數)被約束到SignedNumber,它必須符合ExpressibleByIntegerLiteral - 這讓我們能夠直接傳遞給它字面的整數。

例如:

func genericAdvancedByTen<T : Strideable>(value: T) -> T { 
    // Utilise the fact that T.Stride is ExpressibleByIntegerLiteral. 
    return value.advanced(by: 10) 
} 

print(genericAdvancedByTen(value: 2))  // 12 
print(genericAdvancedByTen(value: 3.14)) // 13.14