swift-使用协议转换数组与ArraySlice

时间:2018-10-28 08:03:01

标签: arrays swift

在此代码段中

protocol MyProtocol {}
extension Int: MyProtocol {}

let a: Array<MyProtocol> = Array<Int>()
let b: ArraySlice<MyProtocol> = a[...]
let c: Array<Int> = a as! Array<Int>
let d: ArraySlice<Int> = b as! ArraySlice<Int>

d发出Cast from 'ArraySlice<MyProtocol>' to unrelated type 'ArraySlice<Int>' always fails警告。

为什么切片不能以与原始数组相同的方式进行转换?可以修改此代码段以将数组转换行为赋予Slice吗?

2 个答案:

答案 0 :(得分:1)

这基本上是由于Swift中泛型方差的工作原理。

Only few types are variant in Swift,包括Array<T>Set<T>。大多数其他类型以及您定义的类型都是不变的。

不变性意味着T<A>T<B>是不相关的类型,即使AB是相关的。

Array<T>Set<T>是协变的,这意味着如果Array<A>是{的子类型,则可以将Array<B>分配给类型A的变量。 {1}}。您可以使用B强制其沿另一方向移动(就像您在第三行中所做的那样)。

与许多其他类型一样,

as!只是不变的。您需要执行以下操作才能转换:

ArraySlice<T>

答案 1 :(得分:0)

作为@Sweeper的正确答案的附录答案,此方法适用于正在寻找类型灵活的性能按引用复制数组的人,我最终推出了一种将数组包装在类中并公开一些API的解决方案用于数组。

这不是一个很好的解决方案,但是可以满足我的需要。 Boo Apple没有为这类事情保持API的一致性。

class ArrayReference<T>: Collection {
    private(set) var array : Array<T>

    init(_ encapsulating: Array<T>? = nil) {
        self.array = encapsulating ?? []
    }

    var startIndex: Int {
        get {
            return array.startIndex
        }
    }

    var endIndex: Int {
        get {
            return array.endIndex
        }
    }

    var count : Int {
        get {
            return array.count
        }
    }

    func index(after i: Int) -> Int {
        return array.index(after: i)
    }

    subscript (index: Int) -> T {
        get { return array[index] }
        set(newValue) { array[index] = newValue }
    }

    func append(_ newValue: T) {
        array.append(newValue)
    }

    func removeAll() {
        array.removeAll()
    }

    var first: T? {
        if array.count > 0 {
            return array[0]
        } else {
            return nil
        }
    }

    var last: T? {
        if array.count > 0 {
            return array[array.count - 1]
        } else {
            return nil
        }
    }

    func asType<C>(_ type: C.Type) -> ArrayReference<C>? {
        if let array = self.array as? Array<C> {
            return ArrayReference<C>(array)
        } else {
            return nil
        }
    }
}

extension ArrayReference: Equatable where T: Equatable {
    static func == (lhs: ArrayReference<T>, rhs: ArrayReference<T>) -> Bool {
        if lhs.count == rhs.count {
            var equal = true
            for (lhs, rhs) in zip(lhs, rhs) {
                equal = equal && (lhs == rhs)
            }
            return equal
        } else {
            return false
        }
    }
}