Tengo un problema con el siguiente código:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
el resultado de generic1 (nombre) al error del compilador "No se puede especializar explícitamente una función genérica"
¿Hay alguna forma de evitar este error? No puedo cambiar la firma de la función generic1, por lo tanto, debería ser (String) -> Void
T
utiliza en absolutogeneric1()
? ¿Cómo llamaría a esa función para que el compilador pueda inferir el tipo?func foo<T>() -> T { ... }
que hace algo con un objetot
de tipoT
y el retornot
. La especialización explícitaT
permitiríat1
injerirsevar t1 = foo<T>()
. Ojalá hubiera una forma de llamar a las funciones de esta manera también. C # lo permiteRespuestas:
También tuve este problema y encontré una solución para mi caso.
En este artículo el autor tiene el mismo problema.
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
Entonces, el problema parece ser que el compilador necesita inferir el tipo de T de alguna manera. Pero no está permitido usar genérico <type> (params ...).
Normalmente, el compilador puede buscar el tipo de T, escaneando los tipos de parámetros porque aquí es donde T se usa en muchos casos.
En mi caso fue un poco diferente, porque el tipo de retorno de mi función era T. En su caso, parece que no ha usado T en absoluto en su función. Supongo que acaba de simplificar el código de ejemplo.
Entonces tengo la siguiente función
func getProperty<T>( propertyID : String ) -> T
Y en el caso de, por ejemplo
getProperty<Int>("countProperty")
el compilador me da el error:
Entonces, para darle al compilador otra fuente de información para inferir el tipo de T, debe declarar explícitamente el tipo de variable en la que se guarda el valor de retorno.
var value : Int = getProperty("countProperty")
De esta forma, el compilador sabe que T tiene que ser un número entero.
Entonces, creo que, en general, simplemente significa que si especifica una función genérica, al menos debe usar T en sus tipos de parámetros o como un tipo de retorno.
fuente
func updateProperty<T>( propertyID : String )
let value = foo() as? Type
para poder usarlo en unaif
oguard
el resultado es opcional, pero no lo hace ...Rápido 5
Normalmente, hay muchas formas de definir funciones genéricas. Pero se basan en una condición que
T
debe usarseparameter
como unreturn type
.extension UIViewController { class func doSomething<T: UIView>() -> T { return T() } class func doSomethingElse<T: UIView>(value: T) { // Note: value is a instance of T } class func doLastThing<T: UIView>(value: T.Type) { // Note: value is a MetaType of T } }
Después de eso, debemos proporcionar
T
al llamar.let result = UIViewController.doSomething() as UIImageView // Define `T` by casting, as UIImageView let result: UILabel = UIViewController.doSomething() // Define `T` with property type, as UILabel UIViewController.doSomethingElse(value: UIButton()) // Define `T` with parameter type, as UIButton UIViewController.doLastThing(value: UITextView.self) // Define `T` with parameter type, as UITextView
Árbitro:
fuente
self.result = UIViewController.doSomething()
funciona por sí solo siempre que haya escrito la propiedad cuando la declare.doLastThing<UITextView>()
convierte en SwiftdoLastThing(UITextView.self)
, que no es ... al menos lo peor. Mejor que tener que escribir explícitamente resultados complicados. Gracias por la solución.La solución es tomar el tipo de clase como parámetro (como en Java)
Para que el compilador sepa con qué tipo está tratando, pase la clase como argumento
extension UIViewController { func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){ let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType before?(controller) self.navigationController?.pushViewController(controller, animated: true) } }
Llamar como:
self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: { controller in controller.user = self.notification.sender })
fuente
No necesita un genérico aquí ya que tiene tipos estáticos (String como parámetro), pero si desea que una función genérica llame a otra, puede hacer lo siguiente.
Usar métodos genéricos
func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T { if let existing = fetchExisting(type) { return existing } else { return createNew(type) } } func fetchExisting<T: NSManagedObject>(type: T.Type) -> T { let entityName = NSStringFromClass(type) // Run query for entiry } func createNew<T: NSManagedObject>(type: T.Type) -> T { let entityName = NSStringFromClass(type) // create entity with name }
Uso de una clase genérica (menos flexible ya que el genérico se puede definir para 1 tipo solo por instancia)
class Foo<T> { func doStuff(text: String) -> T { return doOtherStuff(text) } func doOtherStuff(text: String) -> T { } } let foo = Foo<Int>() foo.doStuff("text")
fuente
Creo que cuando especificas una función genérica debes especificar algunos de los parámetros de tipo T, como sigue:
func generic1<T>(parameter: T) { println("OK") } func generic2<T>(parameter: T) { generic1(parameter) }
y si desea llamar al método handle (), puede hacerlo escribiendo el protocolo y especificando la restricción de tipo para T:
protocol Example { func handle() -> String } extension String: Example { func handle() -> String { return "OK" } } func generic1<T: Example>(parameter: T) { println(parameter.handle()) } func generic2<T: Example>(parameter: T) { generic1(parameter) }
por lo que puede llamar a esta función genérica con String:
generic2("Some")
y se compilará
fuente
Hasta ahora, mi mejor práctica personal utilizó la respuesta de @orkhan-alikhanov. Hoy en día, cuando se mira en SwiftUI y cómo
.modifier()
y laViewModifier
lleva a cabo, he encontrado otra manera (o es más una solución alternativa?)Simplemente envuelva la segunda función en un
struct
.Ejemplo:
Si este le da la opción "No se puede especializar explícitamente una función genérica"
func generic2<T>(name: String){ generic1<T>(name) }
Este podría ayudar. Envuelva la declaración de
generic1
en unstruct
:struct Generic1Struct<T> { func generic1(name: String) {## do, whatever it needs with T ##} }
y llámalo con:
func generic2<T>(name : String){ Generic1Struct<T>().generic1(name: name) }
Observaciones:
struct
es un buen ejemplo. La solución alternativa aquí no tiene un poco más de información, pero pasa el compilador. ¿Misma información, pero resultados diferentes? Entonces algo anda mal. Si es un error del compilador, se puede arreglar.fuente
Tuve un problema similar con mi función de clase genérica
class func retrieveByKey<T: GrandLite>(key: String) -> T?
.No podría llamarlo
let a = retrieveByKey<Categories>(key: "abc")
donde Categorías es una subclase de GrandLite.let a = Categories.retrieveByKey(key:"abc")
devolvió GrandLite, no Categorías. Las funciones genéricas no infieren el tipo en función de la clase que las llama.class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?
me dio un error cuando lo intentélet a = Categories.retrieveByKey(aType: Categories, key: "abc")
me dio un error que no podía convertir Categorías.Tipo a GrandLite, aunque Categorías es una subclase de GrandLite. SIN EMBARGO...class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?
Funcionó si probélet a = Categories.retrieveByKey(aType: [Categories](), key: "abc")
aparentemente una asignación explícita de una subclase no funciona, pero una asignación implícita usando otro tipo genérico (matriz) sí funciona en Swift 3.fuente
aType
como instancia deT
, en lugar de colocarseCategories
. Ejlet a = Categories.retrieveByKey(aType: Categories(), key: "abc")
. : . Otra solución es definiraType: T.Type
. Luego llame al método comolet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")