Saya memiliki masalah dengan kode berikut:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
hasil generic1 (nama) ke kesalahan penyusun "Tidak dapat secara eksplisit mengkhususkan fungsi generik"
Adakah cara untuk menghindari kesalahan ini? Saya tidak bisa mengubah tanda tangan dari fungsi generic1, oleh karena itu harus (String) -> Void
T
digunakan sama sekaligeneric1()
? Bagaimana Anda memanggil fungsi itu, sehingga kompilator dapat menyimpulkan jenisnya?func foo<T>() -> T { ... }
yang melakukan sesuatu dengan objekt
bertipeT
dan returnt
. Mengkhususkan secara eksplisitT
akan memungkinkant1
untuk digangguvar t1 = foo<T>()
. Saya berharap ada cara untuk memanggil fungsi dengan cara ini juga. C # memungkinkannyaJawaban:
Saya juga mengalami masalah ini dan saya menemukan solusi untuk kasus saya.
Pada artikel ini penulis memiliki masalah yang sama
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
Jadi masalahnya tampaknya, kompilator perlu menyimpulkan tipe T entah bagaimana caranya. Tetapi tidak diperbolehkan untuk hanya menggunakan <type> (params ...) generik.
Biasanya, kompilator dapat mencari tipe T, dengan memindai tipe parameter karena di sinilah T digunakan dalam banyak kasus.
Dalam kasus saya itu sedikit berbeda, karena tipe kembalian dari fungsi saya adalah T. Dalam kasus Anda, tampaknya Anda belum menggunakan T sama sekali dalam fungsi Anda. Saya kira Anda baru saja menyederhanakan kode contoh.
Jadi saya memiliki fungsi berikut
func getProperty<T>( propertyID : String ) -> T
Dan dalam kasus, misalnya
getProperty<Int>("countProperty")
kompiler memberi saya kesalahan:
Jadi, untuk memberi kompiler sumber informasi lain untuk menyimpulkan jenis T, Anda harus secara eksplisit mendeklarasikan jenis variabel tempat nilai kembalian disimpan.
var value : Int = getProperty("countProperty")
Dengan cara ini kompilator mengetahui bahwa T harus berupa integer.
Jadi saya pikir secara keseluruhan itu hanya berarti bahwa jika Anda menentukan fungsi generik Anda harus setidaknya menggunakan T dalam tipe parameter Anda atau sebagai tipe kembali.
sumber
func updateProperty<T>( propertyID : String )
let value = foo() as? Type
sehingga dapat digunakan diif
atauguard
jika hasilnya opsional, tetapi tidak ...Cepat 5
Biasanya ada banyak cara untuk mendefinisikan fungsi generik. Tetapi mereka didasarkan pada kondisi yang
T
harus digunakan sebagaiparameter
, atau sebagai areturn 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 } }
Setelah itu, kita harus menyediakan
T
saat menelepon.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
Ref:
sumber
self.result = UIViewController.doSomething()
bekerja sendiri selama Anda mengetik properti saat Anda mendeklarasikannya.doLastThing<UITextView>()
menjadi SwiftdoLastThing(UITextView.self)
, yang ... bukan yang terburuk, setidaknya. Lebih baik daripada harus mengetikkan hasil yang rumit secara eksplisit. Terima kasih atas solusinya.Solusinya adalah mengambil tipe kelas sebagai parameter (seperti di Java)
Untuk memberi tahu compiler tipe apa yang dia hadapi, berikan kelas sebagai argumen
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) } }
Panggil sebagai:
self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: { controller in controller.user = self.notification.sender })
sumber
Anda tidak memerlukan generik di sini karena Anda memiliki tipe statis (String sebagai parameter), tetapi jika Anda ingin memiliki pemanggilan fungsi generik yang lain, Anda dapat melakukan hal berikut.
Menggunakan metode Generik
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 }
Menggunakan kelas generik (Kurang fleksibel karena generik hanya dapat didefinisikan untuk 1 jenis saja per instance)
class Foo<T> { func doStuff(text: String) -> T { return doOtherStuff(text) } func doOtherStuff(text: String) -> T { } } let foo = Foo<Int>() foo.doStuff("text")
sumber
Saya pikir ketika Anda menentukan fungsi generik Anda harus menentukan beberapa parameter tipe T, seperti berikut:
func generic1<T>(parameter: T) { println("OK") } func generic2<T>(parameter: T) { generic1(parameter) }
dan jika Anda ingin memanggil metode handle (), maka Anda dapat melakukannya dengan menulis protokol, dan menentukan batasan tipe untuk 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) }
jadi Anda dapat memanggil fungsi umum ini dengan String:
generic2("Some")
dan itu akan dikompilasi
sumber
Sejauh ini, praktik terbaik pribadi saya menggunakan jawaban @ orkhan-alikhanov. Hari ini, ketika melihat SwiftUI dan bagaimana
.modifier()
sertaViewModifier
implementasinya, saya menemukan cara lain (atau lebih merupakan solusi?)Cukup bungkus fungsi kedua menjadi a
struct
.Contoh:
Jika yang ini memberi Anda "Tidak dapat secara eksplisit mengkhususkan fungsi generik"
func generic2<T>(name: String){ generic1<T>(name) }
Yang ini mungkin bisa membantu. Bungkus deklarasi
generic1
menjadistruct
:struct Generic1Struct<T> { func generic1(name: String) {## do, whatever it needs with T ##} }
dan menyebutnya dengan:
func generic2<T>(name : String){ Generic1Struct<T>().generic1(name: name) }
Catatan:
struct
ini adalah contoh yang bagus. Solusi di sini tidak memiliki lebih banyak informasi - tetapi meneruskan kompiler. Informasi yang sama, tetapi hasilnya berbeda? Lalu ada yang salah. Jika ini adalah bug kompilator, ini bisa diperbaiki.sumber
Saya memiliki masalah serupa dengan fungsi kelas generik saya
class func retrieveByKey<T: GrandLite>(key: String) -> T?
.Saya tidak bisa menyebutnya di
let a = retrieveByKey<Categories>(key: "abc")
mana Kategori adalah subclass dari GrandLite.let a = Categories.retrieveByKey(key:"abc")
mengembalikan GrandLite, bukan Kategori. Fungsi generik tidak menyimpulkan tipe berdasarkan kelas yang memanggilnya.class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?
memberi saya kesalahan ketika saya mencobalet a = Categories.retrieveByKey(aType: Categories, key: "abc")
memberi saya kesalahan yang tidak dapat mengonversi Kategori. Ketik ke GrandLite, meskipun Kategori adalah subkelas dari GrandLite. NAMUN...class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?
melakukan pekerjaan jika saya mencobalet a = Categories.retrieveByKey(aType: [Categories](), key: "abc")
ternyata tugas eksplisit dari subclass tidak bekerja, tetapi assigment implisit menggunakan jenis generik lain (array) tidak bekerja di Swift 3.sumber
aType
contohT
, bukan menempatkanCategories
dirinya sendiri. Ex:let a = Categories.retrieveByKey(aType: Categories(), key: "abc")
. Solusi lain ditentukanaType: T.Type
. Kemudian panggil metode tersebut sebagailet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")