Использование переменной типа в общем

У меня этот вопрос, за исключением Swift. Как использовать переменную Type в общем?

Я пробовал это:

func intType() -> Int.Type {
    return Int.self
}

func test() {
    var t = self.intType()
    var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}

Это тоже не сработало:

var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.

Есть ли способ сделать это? Я чувствую, что Swift просто не поддерживает его и дает мне несколько неоднозначных сообщений об ошибках.

Изменить. Здесь приведен более сложный пример, где проблему нельзя обойти, используя общий заголовок функции. Конечно, это не имеет смысла, но у меня есть разумное использование для такого рода функций где-то в моем коде и скорее опубликуйте чистый пример вместо моего фактического кода:

func someTypes() -> [Any.Type] {
    var ret = [Any.Type]()
    for (var i = 0; i<rand()%10; i++) {
        if (rand()%2 == 0){ ret.append(Int.self) }
        else {ret.append(String.self) }
    }
    return ret
}

func test() {
    var ts = self.someTypes()

    for t in ts {
        var arr = Array<t>()
    }
}

Ответы

Ответ 1

Swift статическая типизация означает, что тип переменной должен быть известен во время компиляции.

В контексте общей функции func foo<T>() { ... } T выглядит как переменная, но ее тип фактически известен во время компиляции на основе того, откуда вызывается функция. Поведение Array<T>() зависит от T, но эта информация известна во время компиляции.

При использовании протоколов Swift использует динамическую рассылку, поэтому вы можете написать Array<MyProtocol>(), и массив просто хранит ссылки на вещи, которые реализуют MyProtocol - поэтому, когда вы получаете что-то из массива, у вас есть доступ ко всем функциям/переменным/типам, требуемым MyProtocol.

Но если T на самом деле является переменной вида Any.Type, Array<T>() бессмысленна, так как ее тип на самом деле не известен во время компиляции. (Так как Array является общей структурой, компилятор должен знать, какой тип использовать в качестве общего параметра, но это невозможно.)

Я бы порекомендовал посмотреть некоторые видео с WWDC в этом году:

Я нашел этот слайд особенно полезным для понимания протоколов и динамической отправки:

Ответ 2

Есть способ, и он называется generics. Вы могли бы сделать что-то подобное.

class func foo() {
    test(Int.self)
}

class func test<T>(t: T.Type) {
    var arr = Array<T>()
}

Вам нужно будет намекнуть компилятору на тип, который вы хотите специализировать функцию, так или иначе. Другой способ - с возвратным параметром (отбрасывается в этом случае):

class func foo() {
    let _:Int = test()
}

class func test<T>() -> T {
    var arr = Array<T>()
}

И используя generics для класса (или структуры), вам не нужен дополнительный параметр:

class Whatever<T> {
    var array = [T]() // another way to init the array.
}

let we = Whatever<Int>()

Ответ 3

Ответ

jtbandes - что вы не можете использовать свой текущий подход, потому что Swift статически типизирован - это правильно.

Однако, если вы хотите создать белый список допустимых типов в вашем массиве, например, в enum, вы можете динамически инициализировать разные типы во время выполнения.

Сначала создайте enum допустимых типов:

enum Types {
    case Int
    case String
}

Создайте класс Example. Внесите свою функцию someTypes(), чтобы использовать эти значения перечисления. (Вы можете легко преобразовать массив строк JSON в массив этого перечисления.)

class Example {
    func someTypes() -> [Types] {
        var ret = [Types]()
        for _ in 1...rand()%10 {
            if (rand()%2 == 0){ ret.append(.Int) }
            else {ret.append(.String) }
        }
        return ret
    }

Теперь реализуйте свою тестовую функцию, используя switch для области arr для каждого допустимого типа:

    func test() {
        let types = self.someTypes()

        for type in types {
            switch type {
            case .Int:
                var arr = [Int]()
                arr += [4]

            case .String:
                var arr = [String]()
                arr += ["hi"]
            }
        }
    }
}

Как вы знаете, вы можете в качестве альтернативы объявить arr как [Any] для смешивания типов ( "гетерогенный" случай в ответе jtbandes):

var arr = [Any]()

for type in types {
    switch type {
    case .Int:
        arr += [4]

    case .String:
        arr += ["hi"]
    }
}

print(arr)

Ответ 4

Я сломал бы это с вещами, которые вы уже узнали из первого ответа. Я взял на себя смелость реорганизовать какой-то код. Вот он:

func someTypes<T>(t: T.Type) -> [Any.Type] {
    var ret = [Any.Type]()
    for _ in 0..<rand()%10 {
        if (rand()%2 == 0){ ret.append(T.self) }
        else {
            ret.append(String.self)
        }
    }
    return ret
}


func makeArray<T>(t: T) -> [T] {
    return [T]()
}

func test() {
    let ts = someTypes(Int.self)
    for t in ts {
        print(t)
    }
}

Это несколько работает, но я считаю, что способ сделать это очень неортодоксально. Не могли бы вы использовать отражение (зеркалирование)?