Использование переменной типа в общем
У меня этот вопрос, за исключением 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)
}
}
Это несколько работает, но я считаю, что способ сделать это очень неортодоксально. Не могли бы вы использовать отражение (зеркалирование)?