2015-06-18 36 views
15

Ho this question eccetto per Swift. Come utilizzare una variabile Type in un generico?Utilizzo di una variabile di tipo generica

ho provato questo:

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

Questo non ha funzionato neanche:

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. 

C'è un modo per fare questo? Ho la sensazione che Swift non lo supporta e mi sta dando messaggi di errore un po 'ambigui.

Modifica: Ecco un esempio più complesso in cui il problema non può essere aggirato utilizzando un'intestazione di funzione generica. Naturalmente non ha senso, ma ho un uso ragionevole per questo tipo di funzionalità da qualche parte nel mio codice e preferisce inviare un esempio pulito invece del mio codice vero e proprio:

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

risposta

22

Swift static typing indica che il tipo di una variabile deve essere noto al momento della compilazione.

Nel contesto di una generica funzione func foo<T>() { ... }, T sembra una variabile, ma il suo tipo è effettivamente noto al momento della compilazione in base a dove la funzione è chiamato da . Il comportamento di Array<T>() dipende da T, ma questa informazione è nota al momento della compilazione.

Quando si utilizza protocolli, Swift impiega spedizione dinamica, in modo da poter scrivere Array<MyProtocol>(), e l'array memorizza semplicemente riferimenti a cose che implementano MyProtocol - in modo che quando si ottiene qualcosa fuori dalla matrice, si ha accesso a tutte le funzioni/variabili/tipealie richieste da MyProtocol.

Ma se t è in realtà una variabile di tipo Any.Type, Array<t>() è privo di significato dal suo tipo è in realtà non è noto al momento della compilazione. (Dal Array è una struttura generica, il compilatore deve sapere quale tipo utilizzare come parametro generico, ma questo non è possibile.)

mi sento di raccomandare a guardare alcuni video dal WWDC di quest'anno:

Ho trovato questa diapositiva particolarmente utile per comprendere i protocolli e la spedizione dinamica:

+0

Mmm, quindi questo è il cuore del problema. Immagino che dovrei guardare quei video allora! – sudo

4

C'è un modo ed è chiamato generici. Potresti fare qualcosa del genere.

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

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

Sarà necessario accennare al compilatore al tipo che si desidera specializzarsi la funzione con, in un modo o in un altro. Un altro modo è con param ritorno (scartato in quel caso):

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

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

e l'utilizzo di farmaci generici su una classe (o struct) non è necessario il parametro in più:

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

let we = Whatever<Int>() 
+0

Th è sarebbe risolvere il mio esempio, ma non funziona in generale. Che cosa succede se si dispone di una matrice di tipi variabili da passare? In realtà ho questo, ma volevo pubblicare un semplice esempio. Aggiungerò un esempio in cui i generici non funzioneranno. – sudo

+0

Ancora una buona risposta però. Questo è utile nel caso in cui ne conosca abbastanza durante la compilazione. Ma sto ricevendo tipi su una connessione di rete e sto provando a costruire oggetti da loro, quindi sto cercando la soluzione totalmente generale. – sudo

2

vorrei romperlo giù con le cose che hai già imparato dalla prima risposta. Mi sono preso la libertà di refactoring del codice. Eccolo:

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

Questo è un po 'funzionante, ma credo che il modo per farlo sia molto poco ortodosso. Potresti usare la riflessione (mirroring) invece?

+0

Questo è interessante e creativo, ma non penso sia utilizzabile. Supponiamo che aggiungiate 'var arr = makeArray (t)' nel ciclo '' '' di 'test()'. 'arr' sarà di tipo' Array .Type> ', che non può essere lanciato su alcunché. –

+0

Stranamente, questo fallisce ogni volta che provo a mettere qualcosa di dinamico lì invece di 'Int.self'. In questo modo: 'var type: Any.Type; if (rand()% 2 == 0) {type = Int.self}; else {type = String.self}; let ts = someTypes (type); ' – sudo

+1

E non mi piacciono i messaggi di errore che sto ricevendo per questo. È come se Swift stia inventando le regole. – sudo

4

risposta jtbandes '- che non è possibile utilizzare l'approccio corrente perché Swift è tipizzato staticamente - è corretto.

Tuttavia, se si desidera creare una whitelist di tipi consentiti nell'array, ad esempio in un enum, è possibile inizializzare dinamicamente diversi tipi in fase di esecuzione.

In primo luogo, creare un enum di tipi consentiti:

enum Types { 
    case Int 
    case String 
} 

creare una classe Example. Implementare la funzione someTypes() per utilizzare questi valori enumerati. (Si potrebbe facilmente trasformare una matrice JSON di stringhe in un array di questo enum.)

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 
    } 

ora implementare la funzione di test, utilizzando switch a scopo arr per ogni tipo di progetto:

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"] 
      } 
     } 
    } 
} 

Come si può sapere, si potrebbe in alternativa, dichiarare arr come [Any] per mescolare i tipi (il caso "eterogeneo" in risposta jtbandes'):

var arr = [Any]() 

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

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

print(arr) 
Problemi correlati