通过符合 Swift 2 中的协议扩展类型化数组

Extending typed array by conforming to a protocol in Swift 2

我想扩展类型化数组 Array<SomeType> 以使其符合协议 SomeProtocol。现在我知道您可以像下面这样扩展类型化数组:

extension Array where Element: SomeType { ... }

您还可以扩展对象以符合如下协议:

extension Array: SomeProtocol { ...  }

但我无法弄清楚让类型化数组符合协议的正确语法是什么,例如:

extension (Array where Element: SomeType): SomeProtocol { ... }

有 Swift 2 位专家知道如何做到这一点吗?

您不能将大量逻辑应用于一致性。它要么符合,要么不符合。但是,您可以对扩展应用一些逻辑。下面的代码可以很容易地设置一致性的具体实现。哪个才是重点。

稍后用作类型约束。

class SomeType { }

这是你的协议

protocol SomeProtocol {

    func foo()

}

这是协议的扩展。 foo()SomeProtocol 扩展中的实现创建了一个默认值。

extension SomeProtocol {

    func foo() {
        print("general")
    }
}

现在 Array 使用 foo() 的默认实现符合 SomeProtocol。所有数组现在都有 foo() 作为方法,这不是很优雅。但它什么也没做,所以它不会伤害任何人。

extension Array : SomeProtocol {}

现在很酷的东西: 如果我们为 Array 创建一个带有 Element 类型约束的扩展,我们可以覆盖 foo()

的默认实现
extension Array where Element : SomeType {
    func foo() {
        print("specific")
    }
}

测试:

let arrayOfInt = [1,2,3]
arrayOfInt.foo() // prints "general"

let arrayOfSome = [SomeType()]
arrayOfSome.foo() // prints "specific"

我自己一直在摆弄一些东西,有一些方法可以在某种程度上模仿您正在寻找的行为。


方法 #1

定义一个协议 SomeType 作为您希望 SomeProtocolArray<SomeType> 扩展涵盖的类型的类型约束;其中后者包含一些您希望扩展的简洁方法的蓝图 Array.

protocol SomeType {
    var intValue: Int { get }
    init(_ value: Int)
    func *(lhs: Self, rhs: Self) -> Self
    func +=(inout lhs: Self, rhs: Self)
}

extension Int : SomeType { var intValue: Int { return self } }
extension Double : SomeType { var intValue: Int { return Int(self) } }
    /* Let's not extend 'Float' for now
extension Float : MyTypes { var intValue: Int { return Int(self) } } */

protocol SomeProtocol {
    func foo<T: SomeType>(a: [T]) -> Int?
}

现在,您可以将 Array 扩展到 SomeProtocol,并且通过使用 is 关键字,您可以断言您的泛型 T(受 SomeType) 和 Self 的元素通过使用 is 关键字属于同一类型,如果为真,则后跟显式转换:

extension Array : SomeProtocol {
    func foo<T: SomeType>(a: [T]) -> Int? {
        /* [T] is Self? proceed, otherwise return nil */
        if let b = self.first {
            if b is T && self.count == a.count {
                var myMultSum: T = T(0)

                for (i, sElem) in self.enumerate() {
                    myMultSum += (sElem as! T) * a[i]
                }
                return myMultSum.intValue
            }
        }
        return nil
    }
}

我们现在用 SomeType 的元素扩展了 Array,并在协议 SomeProtocol 中绘制了函数 foo(...)

/* Tests */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]

let arr1f : [Float] = [1.0, 2.0, 3.0]
let arr2f : [Float] = [-3.0, -2.0, 1.0]

func bar<U: SomeType> (arr1: [U], _ arr2: [U]) -> Int? {
    return arr1.foo(arr2)
}
let myInt1d = bar(arr1d, arr2d) // -4, OK

let myInt1f = bar(arr1f, arr2f)
    /* Compile time error: "Cannot convert value of type '[Float]'
       to expected argument type '[_]'"                            */

好的!我们预计这里会出现最终编译时错误,因为 'Float' 不符合 SomeType 协议。


方法 #2

现在介绍另一种方法:我基于 this excellent post by Milen Dzhumerov 之后的泛型,此处针对数组进行了调整,并使用了一些不同的扩展方法示例。

对于这个例子,我们正在为 Array:s 类型 DoubleFloat 实现一个 "generic" 协议扩展,由类型约束协议 SomeType

protocol SomeType {
    init(_ value: Int)
    init(_ value: Double)
    init(_ value: Float)
    func == (lhs: Self, rhs: Self) -> Bool
}

extension Double: SomeType {}
extension Float: SomeType {}

protocol GenericProtocol {
    typealias AbstractType : SequenceType
    func repeatNumberNumberManyTimes(arg: Int) -> AbstractType
    func removeRandomElement(arg: AbstractType) -> AbstractType
    func countNumberOf42s(arg: AbstractType) -> Int

}

使用结构将GenericProtocol转发给AbstractType(这里符合SequenceType),并在后者中实现协议蓝图:

struct SomeArrayProtocol<T: SequenceType> : GenericProtocol {
    private let _repeatNumberNumberManyTimes : (Int) -> T
    private let _removeRandomElement : (T) -> T
    private let _countNumberOf42s : (T) -> Int

    init<P : GenericProtocol where P.AbstractType == T>(_ dep : P) {
        _repeatNumberNumberManyTimes = dep.repeatNumberNumberManyTimes
        _removeRandomElement = dep.removeRandomElement
        _countNumberOf42s = dep.countNumberOf42s
    }

    func repeatNumberNumberManyTimes(arg: Int) -> T {
        return _repeatNumberNumberManyTimes(arg)
    }

    func removeRandomElement(arg: T) -> T {
        return _removeRandomElement(arg)
    }

    func countNumberOf42s(arg: T) -> Int {
        return _countNumberOf42s(arg)
    }
}

在另一个结构中实现 GenericProtocol 蓝图的实际方法,其中泛型现在被限制为 SomeType 类型约束(协议)。请注意,这是模仿您希望的冷杉(但直接无法实现)形式的部分 extension (Array where Element: SomeType): SomeProtocol { ... }:

struct SomeArrayGenericExtensions<T: SomeType> : GenericProtocol {
    typealias AbstractType = Array<T>
    func repeatNumberNumberManyTimes(arg: Int) -> [T] {
        return Array<T>(count: arg, repeatedValue: T(arg))
    }
    func removeRandomElement(arg: [T]) -> [T] {
        var output = [T]()
        let randElemRemoved = Int(arc4random_uniform(UInt32(arg.count-1)))
        for (i,element) in arg.enumerate() {
            if i != randElemRemoved {
                output.append(element)
            }
        }
        return output
    }
    func countNumberOf42s(arg: [T]) -> Int {
        var output = 0
        for element in arg {
            if element == T(42) {
                output++
            }
        }
        return output
    }
}

最后,一些测试:

let myGenericExtensionUsedForDouble : SomeArrayProtocol<Array<Double>> = SomeArrayProtocol(SomeArrayGenericExtensions())
let myGenericExtensionUsedForFloat : SomeArrayProtocol<Array<Float>> = SomeArrayProtocol(SomeArrayGenericExtensions())
// let myGenericExtensionUsedForInt : SomeArrayProtocol<Array<Int>> = SomeArrayProtocol(SomeArrayGenericExtensions()) // Error! Int not SomeType, OK!

var myDoubleArr = [10.1, 42, 15.8, 42.0, 88.3]
let my10EntriesOfTenDoubleArr = myGenericExtensionUsedForDouble.repeatNumberNumberManyTimes(10) // ten 10:s
let myFloatArr : Array<Float> = [1.3, 5, 8.8, 13.0, 28, 42.0, 42.002]
let myIntArr = [1, 2, 3]

let a = myGenericExtensionUsedForDouble.countNumberOf42s(myDoubleArr) // 2
let b = myGenericExtensionUsedForFloat.countNumberOf42s(myFloatArr) // 1

myDoubleArr = myGenericExtensionUsedForDouble.removeRandomElement(myDoubleArr) // [10.1, 15.8, 42.0, 88.3]

我有点不确定上面的方法 2 是否真的对数组有一些实际应用(在米兰的报道中,他处理非序列类型,也许更有用);这是相当多的工作,但没有那么多额外的收益。然而,它可以是一个有启发性且非常有趣的练习:)

在 Swift 的更新版本中,可以这样写:

extension Array: SomeProtocol where Element == SomeType { ... }

不确定在哪个 Swift 版本中这成为可能,但以下在 Swift 4.1

中有效
class SomeType { }

protocol SomeProtocol {
    func foo()
}

extension Array: SomeProtocol where Element == SomeType {
    func foo() {
        print("foo")
    }
}

let arrayOfSome = [SomeType()]
arrayOfSome.foo() // prints "foo"

let arrayOfInt = [1,2,3]
arrayOfInt.foo() // Will not compile: '[Int]' is not convertible to 'Array<SomeType>'

(我知道这个问题特别要求Swift 2,但我添加这个以供参考)