swift - приведение массива к массиву с использованием протоколов - PullRequest
0 голосов
/ 28 октября 2018

В этом фрагменте кода

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.

Почему нельзя вырезать фрагмент так же, как исходный массив?Может ли этот фрагмент быть изменен, чтобы придать срезу поведение Array?

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

В качестве дополнения к правильному ответу @Sweeper для людей, которые ищут гибкие по типу производительные массивы copy-by-ref, я закончил поиском решения, которое оборачивает массив в класс и предоставляет некоторые 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
        }
    }
}
0 голосов
/ 28 октября 2018

Это в основном связано с тем, как работает универсальная дисперсия в Swift.

Только несколько типов являются вариациями в Swift , включая Array<T> и Set<T>.Большинство других типов и определяемые вами типы являются инвариантами.

Инвариантность означает, что T<A> и T<B> являются несвязанными типами, даже если A и B связаны между собой.

Array<T> и Set<T> являются ковариантными, что означает, что Array<A> можно присвоить переменной типа Array<B>, если A является подтипом B.Вы можете заставить его идти другим путем (как вы это делали в третьей строке), используя as!.

ArraySlice<T>, как и многие другие типы, просто инвариантно.Вам нужно сделать это для конвертации:

let d: ArraySlice<Int> = ArraySlice(b.map { $0 as! Int })
...