У меня проблема со следующим кодом:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
результат generic1 (name) для ошибки компилятора "Невозможно явно специализировать универсальную функцию"
Есть ли способ избежать этой ошибки? Я не могу изменить сигнатуру функции generic1, поэтому она должна быть (String) -> Void
T
Используется ли вообще тип заполнителяgeneric1()
? Как бы вы вызывали эту функцию, чтобы компилятор мог определить тип?func foo<T>() -> T { ... }
пример : что-то делает с объектомt
типаT
и возвращениемt
. Явная специализацияT
позволилаt1
бы сделать выводvar t1 = foo<T>()
. Я бы хотел, чтобы был способ вызывать функции таким же образом. C # позволяет этоОтветы:
У меня тоже была эта проблема, и я нашел способ ее решения.
В этой статье у автора та же проблема
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
Итак, проблема, похоже, в том, что компилятор должен каким-то образом вывести тип T. Но нельзя просто использовать общий <тип> (параметры ...).
Обычно компилятор может искать тип T, просматривая типы параметров, потому что именно здесь T используется во многих случаях.
В моем случае это было немного иначе, потому что тип возвращаемого значения моей функции был T. В вашем случае кажется, что вы вообще не использовали T в своей функции. Думаю, вы просто упростили пример кода.
Итак, у меня есть следующая функция
func getProperty<T>( propertyID : String ) -> T
А в случае, например,
getProperty<Int>("countProperty")
компилятор выдает ошибку:
Итак, чтобы предоставить компилятору другой источник информации, из которого можно сделать вывод о типе T, вы должны явно объявить тип переменной, в которой сохраняется возвращаемое значение.
var value : Int = getProperty("countProperty")
Таким образом, компилятор знает, что T должно быть целым числом.
Поэтому я думаю, что в целом это просто означает, что если вы указываете универсальную функцию, вы должны хотя бы использовать T в своих типах параметров или в качестве возвращаемого типа.
источник
func updateProperty<T>( propertyID : String )
let value = foo() as? Type
чтобы его можно было использовать в a,if
иначеguard
результат не является обязательным, но он не ...Swift 5
Обычно существует множество способов определения общих функций. Но они основаны на условии, которое
T
должно использоваться какparameter
или какreturn 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 } }
После этого мы должны предоставить
T
при звонке.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
Ссылка:
источник
self.result = UIViewController.doSomething()
работает сам по себе, если вы ввели свойство при его объявлении.doLastThing<UITextView>()
становится SwiftdoLastThing(UITextView.self)
, что, по крайней мере, не самое худшее. Лучше, чем явно вводить сложные результаты. Спасибо за обходной путь.Решение принимает тип класса в качестве параметра (как в Java)
Чтобы компилятор знал, с каким типом он имеет дело, передайте класс в качестве аргумента
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) } }
Звоните как:
self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: { controller in controller.user = self.notification.sender })
источник
Здесь вам не нужен общий тип, поскольку у вас есть статические типы (String в качестве параметра), но если вы хотите, чтобы общая функция вызывала другую, вы можете сделать следующее.
Использование общих методов
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 }
Использование универсального класса (менее гибкий, поскольку универсальный можно определить только для 1 типа для каждого экземпляра)
class Foo<T> { func doStuff(text: String) -> T { return doOtherStuff(text) } func doOtherStuff(text: String) -> T { } } let foo = Foo<Int>() foo.doStuff("text")
источник
Я думаю, что когда вы указываете универсальную функцию, вы должны указать некоторые параметры типа T, например:
func generic1<T>(parameter: T) { println("OK") } func generic2<T>(parameter: T) { generic1(parameter) }
и если вы хотите вызвать метод handle (), вы можете сделать это, написав протокол и указав ограничение типа для 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) }
поэтому вы можете вызвать эту общую функцию с помощью String:
generic2("Some")
и он скомпилирует
источник
До сих пор в моей личной передовой практике использовался ответ @orkhan-alikhanov. Сегодня, глядя на SwiftUI и как
.modifier()
иViewModifier
реализуются, я нашел еще один способ (или это скорее обходной путь?)Просто оберните вторую функцию в
struct
.Пример:
Если это дает вам сообщение «Невозможно явно специализировать универсальную функцию»
func generic2<T>(name: String){ generic1<T>(name) }
Это может помочь. Оберните объявление
generic1
вstruct
:struct Generic1Struct<T> { func generic1(name: String) {## do, whatever it needs with T ##} }
и назовите его:
func generic2<T>(name : String){ Generic1Struct<T>().generic1(name: name) }
Примечания:
struct
являются хорошими примерами. Обходной путь здесь не содержит дополнительной информации, но передается компилятору. Та же информация, но разные результаты? Тогда что-то не так. Если это ошибка компилятора, ее можно исправить.источник
У меня была аналогичная проблема с моей функцией общего класса
class func retrieveByKey<T: GrandLite>(key: String) -> T?
.Я не мог назвать это
let a = retrieveByKey<Categories>(key: "abc")
там, где Категории - это подкласс GrandLite.let a = Categories.retrieveByKey(key:"abc")
вернул GrandLite, а не категории. Универсальные функции не определяют тип на основе класса, который их вызывает.class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?
дал мне ошибку, когда я попытался,let a = Categories.retrieveByKey(aType: Categories, key: "abc")
дал мне ошибку, что он не может преобразовать Categories.Type в GrandLite, хотя Categories является подклассом GrandLite. ОДНАКО...class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?
действительно сработало, если я попробовалlet a = Categories.retrieveByKey(aType: [Categories](), key: "abc")
явно явное присвоение подкласса, не работает, но неявное присвоение с использованием другого универсального типа (массива) действительно работает в Swift 3.источник
aType
как экземплярT
вместо самогоCategories
себя. Пример:let a = Categories.retrieveByKey(aType: Categories(), key: "abc")
. Другое решение - определитьaType: T.Type
. Затем вызовите метод какlet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")