【问题标题】:Generic function for type that implements common interface实现通用接口的类型的通用函数
【发布时间】:2016-11-21 21:20:27
【问题描述】:

我想增加我对泛型的了解,但遇到了一个我无法解决的问题。我有两种不同的类型(IntDouble)。这两种类型都实现了一个函数advanced(by:)。我想创建一个通用函数来调用给定类型的advanced(by:)

这样做的好处是我可以用一个 genericAdvancedByTen(value:) 替换 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:)方法?

【问题讨论】:

    标签: swift generics


    【解决方案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
    

    【讨论】:

    • 效果很好,我真的很高兴。谢谢! :) 我很困惑,因为这是我第一次看到使用大写字母 Self ,所以这里有一个解释 selfSelf 之间区别的链接:stackoverflow.com/questions/27863810/…
    【解决方案2】:

    无需定义您自己的协议——advanced(by:) 由标准库的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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-28
      • 2022-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多