2015-06-18 3 views
15

У меня есть this question за исключением 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>() 
    } 
} 

ответ

22

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

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

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

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

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

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

+0

Ммм, так что это суть проблемы. Думаю, я должен посмотреть эти видео! – sudo

4

Существует способ, и это называемых дженериками. Вы могли бы сделать что-то подобное.

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>() 
} 

и с помощью дженерик на класс (или структуру), вам не нужны дополнительные параметры:

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

let we = Whatever<Int>() 
+0

Th будет исправлять мой пример, но он вообще не работает. Что делать, если у вас есть массив типов переменного размера, который вы хотите передать? У меня на самом деле это, но я хотел опубликовать простой пример. Я добавлю пример, когда дженерики не будут работать. – sudo

+0

Тем не менее, хороший ответ. Это полезно в случае, когда я знаю достаточно во время компиляции. Но я получаю типы по сетевому соединению и пытаюсь построить из них объекты, поэтому я ищу полностью общее решение. – sudo

2

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

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) 
    } 
} 

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

+0

Это интересно и креативно, но я не думаю, что это можно использовать. Скажем, вы добавляете 'var arr = makeArray (t)' в 'test()' '' 'for' loop. 'arr' будет иметь тип' Array .Type> ', который нельзя использовать для чего-либо. –

+0

Странно, это терпит неудачу, когда я пытаюсь поставить там что-то динамическое вместо 'Int.self'. Например: 'var type: Any.Type; if (rand()% 2 == 0) {type = Int.self}; else {type = String.self}; let ts = someTypes (type); ' – sudo

+1

И мне не нравятся сообщения об ошибках, которые я получаю для этого. Это похоже на то, что Свифт формулирует правила, как есть. – sudo

4

Ответ 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)