web-dev-qa-db-fra.com

Utilisation d'une variable de type dans un générique

J'ai cette question sauf pour Swift. Comment utiliser une variable Type dans un générique?

J'ai essayé ceci:

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

Cela n'a pas fonctionné non plus:

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.

Y a-t-il un moyen de faire cela? J'ai l'impression que Swift ne le supporte tout simplement pas et me donne des messages d'erreur quelque peu ambigus.

Edit : Voici un exemple plus complexe où le problème ne peut pas être contourné en utilisant un en-tête de fonction générique. Bien sûr, cela n'a pas de sens, mais j'ai une utilisation sensée pour ce type de fonctionnalité quelque part dans mon code et je préfère poster un exemple propre au lieu de mon code réel:

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

Swift's typage statique signifie que le type d'une variable doit être connu au moment de la compilation.

Dans le contexte d'une fonction générique func foo<T>() { ... }, [~ # ~] t [~ # ~] ressemble à une variable, mais son type est en fait connu au moment de la compilation en fonction de l'endroit où la fonction est appelée depuis. Le comportement de Array<T>() dépend de T, mais cette information est connue au moment de la compilation.

Lorsque vous utilisez des protocoles, Swift utilise répartition dynamique , vous pouvez donc écrire Array<MyProtocol>(), et le tableau stocke simplement des références à des choses qui implémentent MyProtocol - donc quand vous sortez quelque chose du tableau, vous avez accès à toutes les fonctions/variables/typealiases requises par MyProtocol.

Mais si t est en réalité une variable de type Any.Type, Array<t>() n'a pas de sens puisque son type n'est en fait pas connu au moment de la compilation. (Puisque Array est une structure générique, le compilateur a besoin de savoir quel type utiliser comme paramètre générique, mais ce n'est pas possible.)

Je recommanderais de regarder quelques vidéos de la WWDC cette année:

J'ai trouvé cette diapositive particulièrement utile pour comprendre les protocoles et la répartition dynamique:

26
jtbandes

Il y a un moyen et ça s'appelle les génériques. Vous pourriez faire quelque chose comme ça.

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

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

Vous devrez indiquer au compilateur le type avec lequel vous souhaitez spécialiser la fonction, d'une manière ou d'une autre. Une autre méthode consiste à retourner un paramètre (rejeté dans ce cas):

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

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

Et en utilisant des génériques sur une classe (ou une structure), vous n'avez pas besoin du paramètre supplémentaire:

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

let we = Whatever<Int>()
6
Laurent

Je le décomposerais avec les choses que vous avez déjà apprises de la première réponse. J'ai pris la liberté de refactoriser du code. C'est ici:

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

Cela fonctionne quelque peu, mais je pense que la façon de procéder est très peu orthodoxe. Pourriez-vous utiliser la réflexion (mise en miroir) à la place?

3
Laurent