6

我想扩展一个类型化数组Array<SomeType>,使其符合协议SomeProtocol。现在我知道您可以扩展一个类型化数组,如下所示:

extension Array where Element: SomeType { ... }

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

extension Array: SomeProtocol { ...  }

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

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

任何 Swift 2 专家都知道如何做到这一点?

4

3 回答 3

5

您不能将大量逻辑应用于一致性。它要么符合,要么不符合。但是,您可以对扩展应用一点逻辑。下面的代码可以轻松设置特定的一致性实现。这是重要的部分。

稍后将其用作类型化约束。

class SomeType { }

这是你的协议

protocol SomeProtocol {

    func foo()

}

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

extension SomeProtocol {

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

现在Array符合SomeProtocol使用foo(). 现在所有数组都将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"
于 2015-12-26T03:57:04.263 回答
5

在更新的 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,但我添加这个以供参考)

于 2018-05-18T09:25:32.310 回答
2

我自己一直在摆弄一些这个,并且有一些方法可以在某种程度上模仿你正在寻找的行为。


方法#1

定义一个协议SomeType,作为您希望被您的Array<SomeType>扩展覆盖的类型的类型约束SomeProtocol;后者包含您希望扩展的一些简洁方法的蓝图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?
}

现在,您可以扩展ArraySomeProtocol,并使用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
    }
}

我们现在已经扩展了协议中的功能蓝图的Array元素。SomeTypefoo(...)SomeProtocol

/* 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

现在换一种方法:我基于Milen Dzhumerov 的这篇出色的帖子后面的泛型,这里适用于数组和一些不同的扩展方法示例。

对于这个例子,我们正在为Array:s 类型Doubleor实现一个“通用”协议扩展Float,由类型约束协议表示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

}

使用结构转发GenericProtocolto 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 是否真的对数组有一些实际应用(在米兰的报道中,他处理非序列类型,也许更有用);这是相当多的工作,没有那么多额外的收益。然而,它可能是一个有启发性且非常有趣的练习:)

于 2015-12-26T04:02:13.677 回答