functions - swift generics
Obtener el nombre(cadena) de un tipo genérico en Swift (5)
Tengo una clase genérica de tipo T y me gustaría obtener el nombre del tipo que pasó a la clase cuando se crea una instancia. Aquí hay un ejemplo.
class MyClass<T> {
func genericName() -> String {
// Return the name of T.
}
}
He estado mirando alrededor durante horas y parece que no puedo encontrar ninguna manera de hacer esto. ¿Alguien ha intentado esto todavía?
Cualquier ayuda es muy apreciada.
Gracias
String(describing: T.self)
en Swift 3+
var genericTypeName: String {
return String(describing: T.self)
}
Dentro del tipo genérico, obtenga el nombre del tipo T
convirtiendo T.self
o type(of: T.self)
en un String
. Descubrí que el type(of:)
no era necesario, pero vale la pena tenerlo en cuenta, ya que en otros casos elimina otros detalles sobre el Tipo.
El siguiente ejemplo demuestra cómo obtener el nombre del tipo genérico T
dentro de una estructura y una clase. Incluye código para obtener el nombre del tipo que contiene.
Ejemplo que incluye clase y estructura
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)
Salida de consola
/*
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''
*/
Es posible si su parámetro de tipo implementa un protocolo de denominación común.
En el ejemplo a continuación, el protocolo Named
asegura que el tipo genérico implementa la propiedad de clase de name
.
Tenga en cuenta que esto funciona tanto con clases como con tipos de valores, ya que estos últimos también se pueden ampliar para que se ajusten a los protocolos, como se ilustra con el Int
siguiente.
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
Otra posible solución que podría ayudar a alguien:
Patio de recreo
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()
Puede devolver el nombre de cualquier tipo mediante la interpolación de cadenas:
class MyClass<T> {
func genericName() -> String {
return "/(T.self)"
}
}
Puedes probarlo en un patio de recreo y funciona como se espera:
var someClass = MyClass<String>()
someClass.genericName() // Returns "Swift.String"
Una forma pura y rápida de lograr eso no es posible.
Una posible solución es:
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
}
}
}
Las limitaciones se basan en el hecho de que funciona solo con clases.
Si este es el tipo genérico:
class TestClass {}
NSStringFromClass()
devuelve el nombre completo (incluido el espacio de nombres):
// Prints something like "__lldb_expr_186.TestClass" in playground
NSStringFromClass(TestClass.self)
Es por eso que la función busca la última aparición de la .
personaje.
Probado de la siguiente manera:
var x = MyClass<TestClass>()
x.genericName() // Prints "TestClass"
ACTUALIZACIÓN 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
}