【问题标题】:Extending typed array by conforming to a protocol in Swift 2通过遵循 Swift 2 中的协议来扩展类型化数组
【发布时间】:2015-12-26 00:37:48
【问题描述】:

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

extension Array where Element: SomeType { ... }

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

extension Array: SomeProtocol { ...  }

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

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

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

【问题讨论】:

  • 在这个答案中查看 StringType 协议以扩展字符串数组stackoverflow.com/a/33862452/2303865
  • 带有约束的“数组”类型的扩展不能有继承子句。没有逻辑。你能给我们提供一些有用的例子吗?
  • @JamesHu 您有时间查看我们下面的答案并提供反馈吗?

标签: swift generics swift2 swift-extensions


【解决方案1】:

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

这稍后用作类型化约束。

class SomeType { }

这是你的协议

protocol SomeProtocol {

    func foo()

}

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

extension SomeProtocol {

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

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

extension Array : SomeProtocol {}

现在很酷的东西: 如果我们使用Element 的类型约束创建Array 的扩展,我们可以覆盖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"

【讨论】:

    【解决方案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,但我将其添加以供参考)

    【讨论】:

    【解决方案3】:

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


    方法#1

    定义一个协议SomeType,作为您希望被Array&lt;SomeType&gt; 扩展为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?
    }
    

    现在,您可以将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,由类型约束协议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 是否真的对数组有一些实际应用(在 Milans 的报道中,他处理非序列类型,也许更有用);这是相当多的工作,没有那么多额外的收益。但是,它可能是一个有启发性且非常有趣的练习:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-25
      • 1970-01-01
      相关资源
      最近更新 更多