J'ai une classe générique de type T et j'aimerais obtenir le nom du type qui est passé dans la classe lors de son instanciation. Voici un exemple.
class MyClass<T> {
func genericName() -> String {
// Return the name of T.
}
}
Je regarde depuis des heures et je n'arrive pas à trouver un moyen de le faire. Est-ce que quelqu'un a déjà essayé?
Toute aide est grandement appréciée.
Merci
Un moyen purement Swift pour y parvenir n'est pas possible.
Une solution de contournement possible est la suivante:
class MyClass<T: AnyObject> {
func genericName() -> String {
let fullName: String = NSStringFromClass(T.self)
let range = fullName.rangeOfString(".", options: .BackwardsSearch)
if let range = range {
return fullName.substringFromIndex(range.endIndex)
} else {
return fullName
}
}
}
Les limitations reposent sur le fait que cela fonctionne uniquement avec des classes.
S'il s'agit du type générique:
class TestClass {}
NSStringFromClass()
renvoie le nom complet (y compris l'espace de noms):
// Prints something like "__lldb_expr_186.TestClass" in playground
NSStringFromClass(TestClass.self)
C'est pourquoi la fonction cherche la dernière occurrence du caractère .
.
Testé comme suit:
var x = MyClass<TestClass>()
x.genericName() // Prints "TestClass"
MISE À JOUR Swift 3.0
func genericName() -> String {
let fullName: String = NSStringFromClass(T.self)
let range = fullName.range(of: ".")
if let range = range {
return fullName.substring(from: range.upperBound)
}
return fullName
}
Vous pouvez renvoyer n'importe quel nom de type en utilisant une interpolation de chaîne:
class MyClass<T> {
func genericName() -> String {
return "\(T.self)"
}
}
Vous pouvez l'essayer sur un terrain de jeu et cela fonctionne comme prévu:
var someClass = MyClass<String>()
someClass.genericName() // Returns "Swift.String"
String(describing: T.self)
in Swift 3+var genericTypeName: String {
return String(describing: T.self)
}
Dans le type générique, obtenez le nom du type T
en convertissant T.self
ou type(of: T.self)
en String
. J'ai trouvé que type(of:)
n'était pas nécessaire, mais il vaut la peine de le savoir, car dans d'autres cas, il supprime d'autres détails sur le Type.
L'exemple suivant montre comment obtenir le nom du type générique T
au sein d'une structure et d'une classe. Il comprend un code pour obtenir le nom du type contenant.
struct GenericStruct<T> {
var value: T
var genericTypeName: String {
return String(describing: T.self)
}
var genericTypeDescription: String {
return "Generic Type T: '\(genericTypeName)'"
}
var typeDescription: String {
// type(of:) is necessary here to exclude the struct's properties from the string
return "Type: '\(type(of: self))'"
}
}
class GenericClass<T> {
var value: T
var genericTypeName: String {
return String(describing: T.self)
}
var genericTypeDescription: String {
return "Generic Type T: '\(genericTypeName)'"
}
var typeDescription: String {
let typeName = String(describing: self)
return "Type: '\(typeName)'"
}
init(value: T) {
self.value = value
}
}
enum TestEnum {
case value1
case value2
case value3
}
let intGenericStruct: GenericStruct<Int> = GenericStruct(value: 1)
print(intGenericStruct.typeDescription)
print(intGenericStruct.genericTypeDescription)
let enumGenericStruct: GenericStruct<TestEnum> = GenericStruct(value: .value2)
print(enumGenericStruct.typeDescription)
print(enumGenericStruct.genericTypeDescription)
let intGenericClass: GenericClass<Int> = GenericClass(value: 1)
print(intGenericClass.typeDescription)
print(intGenericClass.genericTypeDescription)
let enumGenericClass: GenericClass<TestEnum> = GenericClass(value: .value2)
print(enumGenericClass.typeDescription)
print(enumGenericClass.genericTypeDescription)
/*
Type: 'GenericStruct<Int>'
Generic Type T: 'Int'
Type: 'GenericStruct<TestEnum>'
Generic Type T: 'TestEnum'
Type: 'GenericClass<Swift.Int>'
Generic Type T: 'Int'
Type: 'GenericClass<TestEnum>'
Generic Type T: 'TestEnum'
*/
Une autre solution possible qui pourrait aider quelqu'un:
Cour de récréation
import Foundation
class ClassType<T> {
static func name () -> String
{
return "\(T.self)".componentsSeparatedByString(".").last!
}
}
class MyClass {
}
func testClassName(){
let className = ClassType<MyClass>.name()
print(className)
}
testClassName()
C'est possible si votre paramètre de type implémente un protocole de nommage commun.
Dans l'exemple ci-dessous, le protocole Named
garantit que le type générique implémente la propriété de classe name
.
Notez que cela fonctionne à la fois avec les classes et les types de valeur car ces derniers peuvent également être étendus pour se conformer aux protocoles, comme illustré par la variable Int
ci-dessous.
protocol Named {
class var name: String { get }
}
class MyClass<T: Named> {
func genericName() -> String {
return T.name
}
}
extension Int: Named {
static var name: String { return "I am an Int" }
}
class Foo: Named {
class var name: String { return "I am a Foo" }
}
enum Drink: Named {
static var name: String { return "I am a Drink" }
}
MyClass<Int>().genericName() // I am an Int
MyClass<Foo>().genericName() // I am a Foo
MyClass<Drink>().genericName() // I am a Drink